1 /* 2 * Copyright (c) 1980, 1986, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 #if 0 36 static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; 37 #endif 38 static const char rcsid[] = 39 "$FreeBSD$"; 40 #endif /* not lint */ 41 42 #include <sys/param.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 46 #include <ufs/ufs/dinode.h> 47 #include <ufs/ufs/dir.h> 48 #include <ufs/ffs/fs.h> 49 50 #include <err.h> 51 #include <errno.h> 52 #include <string.h> 53 #include <ctype.h> 54 #include <fstab.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <unistd.h> 58 59 #include "fsck.h" 60 61 long diskreads, totalreads; /* Disk cache statistics */ 62 63 static void rwerror __P((char *mesg, ufs_daddr_t blk)); 64 65 int 66 ftypeok(dp) 67 struct dinode *dp; 68 { 69 switch (dp->di_mode & IFMT) { 70 71 case IFDIR: 72 case IFREG: 73 case IFBLK: 74 case IFCHR: 75 case IFLNK: 76 case IFSOCK: 77 case IFIFO: 78 return (1); 79 80 default: 81 if (debug) 82 printf("bad file type 0%o\n", dp->di_mode); 83 return (0); 84 } 85 } 86 87 int 88 reply(question) 89 char *question; 90 { 91 int persevere; 92 char c; 93 94 if (preen) 95 pfatal("INTERNAL ERROR: GOT TO reply()"); 96 persevere = !strcmp(question, "CONTINUE"); 97 printf("\n"); 98 if (!persevere && (nflag || fswritefd < 0)) { 99 printf("%s? no\n\n", question); 100 resolved = 0; 101 return (0); 102 } 103 if (yflag || (persevere && nflag)) { 104 printf("%s? yes\n\n", question); 105 return (1); 106 } 107 do { 108 printf("%s? [yn] ", question); 109 (void) fflush(stdout); 110 c = getc(stdin); 111 while (c != '\n' && getc(stdin) != '\n') { 112 if (feof(stdin)) { 113 resolved = 0; 114 return (0); 115 } 116 } 117 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 118 printf("\n"); 119 if (c == 'y' || c == 'Y') 120 return (1); 121 resolved = 0; 122 return (0); 123 } 124 125 /* 126 * Look up state information for an inode. 127 */ 128 struct inostat * 129 inoinfo(inum) 130 ino_t inum; 131 { 132 static struct inostat unallocated = { USTATE, 0, 0 }; 133 struct inostatlist *ilp; 134 int iloff; 135 136 if (inum > maxino) 137 errx(EEXIT, "inoinfo: inumber %d out of range", inum); 138 ilp = &inostathead[inum / sblock.fs_ipg]; 139 iloff = inum % sblock.fs_ipg; 140 if (iloff >= ilp->il_numalloced) 141 return (&unallocated); 142 return (&ilp->il_stat[iloff]); 143 } 144 145 /* 146 * Malloc buffers and set up cache. 147 */ 148 void 149 bufinit() 150 { 151 register struct bufarea *bp; 152 long bufcnt, i; 153 char *bufp; 154 155 pbp = pdirbp = (struct bufarea *)0; 156 bufp = malloc((unsigned int)sblock.fs_bsize); 157 if (bufp == 0) 158 errx(EEXIT, "cannot allocate buffer pool"); 159 cgblk.b_un.b_buf = bufp; 160 initbarea(&cgblk); 161 bufhead.b_next = bufhead.b_prev = &bufhead; 162 bufcnt = MAXBUFSPACE / sblock.fs_bsize; 163 if (bufcnt < MINBUFS) 164 bufcnt = MINBUFS; 165 for (i = 0; i < bufcnt; i++) { 166 bp = (struct bufarea *)malloc(sizeof(struct bufarea)); 167 bufp = malloc((unsigned int)sblock.fs_bsize); 168 if (bp == NULL || bufp == NULL) { 169 if (i >= MINBUFS) 170 break; 171 errx(EEXIT, "cannot allocate buffer pool"); 172 } 173 bp->b_un.b_buf = bufp; 174 bp->b_prev = &bufhead; 175 bp->b_next = bufhead.b_next; 176 bufhead.b_next->b_prev = bp; 177 bufhead.b_next = bp; 178 initbarea(bp); 179 } 180 bufhead.b_size = i; /* save number of buffers */ 181 } 182 183 /* 184 * Manage a cache of directory blocks. 185 */ 186 struct bufarea * 187 getdatablk(blkno, size) 188 ufs_daddr_t blkno; 189 long size; 190 { 191 register struct bufarea *bp; 192 193 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 194 if (bp->b_bno == fsbtodb(&sblock, blkno)) 195 goto foundit; 196 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 197 if ((bp->b_flags & B_INUSE) == 0) 198 break; 199 if (bp == &bufhead) 200 errx(EEXIT, "deadlocked buffer pool"); 201 getblk(bp, blkno, size); 202 /* fall through */ 203 foundit: 204 totalreads++; 205 bp->b_prev->b_next = bp->b_next; 206 bp->b_next->b_prev = bp->b_prev; 207 bp->b_prev = &bufhead; 208 bp->b_next = bufhead.b_next; 209 bufhead.b_next->b_prev = bp; 210 bufhead.b_next = bp; 211 bp->b_flags |= B_INUSE; 212 return (bp); 213 } 214 215 void 216 getblk(bp, blk, size) 217 register struct bufarea *bp; 218 ufs_daddr_t blk; 219 long size; 220 { 221 ufs_daddr_t dblk; 222 223 dblk = fsbtodb(&sblock, blk); 224 if (bp->b_bno != dblk) { 225 flush(fswritefd, bp); 226 diskreads++; 227 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 228 bp->b_bno = dblk; 229 bp->b_size = size; 230 } 231 } 232 233 void 234 flush(fd, bp) 235 int fd; 236 register struct bufarea *bp; 237 { 238 register int i, j; 239 240 if (!bp->b_dirty) 241 return; 242 if (bp->b_errs != 0) 243 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 244 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 245 bp->b_bno); 246 bp->b_dirty = 0; 247 bp->b_errs = 0; 248 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 249 if (bp != &sblk) 250 return; 251 for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 252 bwrite(fswritefd, (char *)sblock.fs_csp[j], 253 fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 254 sblock.fs_cssize - i < sblock.fs_bsize ? 255 sblock.fs_cssize - i : sblock.fs_bsize); 256 } 257 } 258 259 static void 260 rwerror(mesg, blk) 261 char *mesg; 262 ufs_daddr_t blk; 263 { 264 265 if (preen == 0) 266 printf("\n"); 267 pfatal("CANNOT %s: BLK %ld", mesg, blk); 268 if (reply("CONTINUE") == 0) 269 exit(EEXIT); 270 } 271 272 void 273 ckfini(markclean) 274 int markclean; 275 { 276 register struct bufarea *bp, *nbp; 277 int ofsmodified, cnt = 0; 278 279 if (fswritefd < 0) { 280 (void)close(fsreadfd); 281 return; 282 } 283 flush(fswritefd, &sblk); 284 if (havesb && sblk.b_bno != SBOFF / dev_bsize && 285 !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 286 sblk.b_bno = SBOFF / dev_bsize; 287 sbdirty(); 288 flush(fswritefd, &sblk); 289 } 290 flush(fswritefd, &cgblk); 291 free(cgblk.b_un.b_buf); 292 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 293 cnt++; 294 flush(fswritefd, bp); 295 nbp = bp->b_prev; 296 free(bp->b_un.b_buf); 297 free((char *)bp); 298 } 299 if (bufhead.b_size != cnt) 300 errx(EEXIT, "panic: lost %d buffers", bufhead.b_size - cnt); 301 pbp = pdirbp = (struct bufarea *)0; 302 if (sblock.fs_clean != markclean) { 303 sblock.fs_clean = markclean; 304 sbdirty(); 305 ofsmodified = fsmodified; 306 flush(fswritefd, &sblk); 307 fsmodified = ofsmodified; 308 if (!preen) { 309 printf("\n***** FILE SYSTEM MARKED %s *****\n", 310 markclean ? "CLEAN" : "DIRTY"); 311 if (!markclean) 312 rerun = 1; 313 } 314 } else if (!preen && !markclean) { 315 printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); 316 rerun = 1; 317 } 318 if (debug) 319 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 320 totalreads, (int)(diskreads * 100 / totalreads)); 321 (void)close(fsreadfd); 322 (void)close(fswritefd); 323 } 324 325 int 326 bread(fd, buf, blk, size) 327 int fd; 328 char *buf; 329 ufs_daddr_t blk; 330 long size; 331 { 332 char *cp; 333 int i, errs; 334 off_t offset; 335 336 offset = blk; 337 offset *= dev_bsize; 338 if (lseek(fd, offset, 0) < 0) 339 rwerror("SEEK", blk); 340 else if (read(fd, buf, (int)size) == size) 341 return (0); 342 rwerror("READ", blk); 343 if (lseek(fd, offset, 0) < 0) 344 rwerror("SEEK", blk); 345 errs = 0; 346 memset(buf, 0, (size_t)size); 347 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 348 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 349 if (read(fd, cp, (int)secsize) != secsize) { 350 (void)lseek(fd, offset + i + secsize, 0); 351 if (secsize != dev_bsize && dev_bsize != 1) 352 printf(" %ld (%ld),", 353 (blk * dev_bsize + i) / secsize, 354 blk + i / dev_bsize); 355 else 356 printf(" %ld,", blk + i / dev_bsize); 357 errs++; 358 } 359 } 360 printf("\n"); 361 if (errs) 362 resolved = 0; 363 return (errs); 364 } 365 366 void 367 bwrite(fd, buf, blk, size) 368 int fd; 369 char *buf; 370 ufs_daddr_t blk; 371 long size; 372 { 373 int i; 374 char *cp; 375 off_t offset; 376 377 if (fd < 0) 378 return; 379 offset = blk; 380 offset *= dev_bsize; 381 if (lseek(fd, offset, 0) < 0) 382 rwerror("SEEK", blk); 383 else if (write(fd, buf, (int)size) == size) { 384 fsmodified = 1; 385 return; 386 } 387 resolved = 0; 388 rwerror("WRITE", blk); 389 if (lseek(fd, offset, 0) < 0) 390 rwerror("SEEK", blk); 391 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 392 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 393 if (write(fd, cp, (int)dev_bsize) != dev_bsize) { 394 (void)lseek(fd, offset + i + dev_bsize, 0); 395 printf(" %ld,", blk + i / dev_bsize); 396 } 397 printf("\n"); 398 return; 399 } 400 401 /* 402 * allocate a data block with the specified number of fragments 403 */ 404 ufs_daddr_t 405 allocblk(frags) 406 long frags; 407 { 408 int i, j, k, cg, baseblk; 409 struct cg *cgp = &cgrp; 410 411 if (frags <= 0 || frags > sblock.fs_frag) 412 return (0); 413 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 414 for (j = 0; j <= sblock.fs_frag - frags; j++) { 415 if (testbmap(i + j)) 416 continue; 417 for (k = 1; k < frags; k++) 418 if (testbmap(i + j + k)) 419 break; 420 if (k < frags) { 421 j += k; 422 continue; 423 } 424 cg = dtog(&sblock, i + j); 425 getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 426 if (!cg_chkmagic(cgp)) 427 pfatal("CG %d: BAD MAGIC NUMBER\n", cg); 428 baseblk = dtogd(&sblock, i + j); 429 for (k = 0; k < frags; k++) { 430 setbmap(i + j + k); 431 clrbit(cg_blksfree(cgp), baseblk + k); 432 } 433 n_blks += frags; 434 if (frags == sblock.fs_frag) 435 cgp->cg_cs.cs_nbfree--; 436 else 437 cgp->cg_cs.cs_nffree -= frags; 438 cgdirty(); 439 return (i + j); 440 } 441 } 442 return (0); 443 } 444 445 /* 446 * Free a previously allocated block 447 */ 448 void 449 freeblk(blkno, frags) 450 ufs_daddr_t blkno; 451 long frags; 452 { 453 struct inodesc idesc; 454 455 idesc.id_blkno = blkno; 456 idesc.id_numfrags = frags; 457 (void)pass4check(&idesc); 458 } 459 460 /* 461 * Find a pathname 462 */ 463 void 464 getpathname(namebuf, curdir, ino) 465 char *namebuf; 466 ino_t curdir, ino; 467 { 468 int len; 469 register char *cp; 470 struct inodesc idesc; 471 static int busy = 0; 472 473 if (curdir == ino && ino == ROOTINO) { 474 (void)strcpy(namebuf, "/"); 475 return; 476 } 477 if (busy || 478 (inoinfo(curdir)->ino_state != DSTATE && 479 inoinfo(curdir)->ino_state != DFOUND)) { 480 (void)strcpy(namebuf, "?"); 481 return; 482 } 483 busy = 1; 484 memset(&idesc, 0, sizeof(struct inodesc)); 485 idesc.id_type = DATA; 486 idesc.id_fix = IGNORE; 487 cp = &namebuf[MAXPATHLEN - 1]; 488 *cp = '\0'; 489 if (curdir != ino) { 490 idesc.id_parent = curdir; 491 goto namelookup; 492 } 493 while (ino != ROOTINO) { 494 idesc.id_number = ino; 495 idesc.id_func = findino; 496 idesc.id_name = ".."; 497 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 498 break; 499 namelookup: 500 idesc.id_number = idesc.id_parent; 501 idesc.id_parent = ino; 502 idesc.id_func = findname; 503 idesc.id_name = namebuf; 504 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 505 break; 506 len = strlen(namebuf); 507 cp -= len; 508 memmove(cp, namebuf, (size_t)len); 509 *--cp = '/'; 510 if (cp < &namebuf[MAXNAMLEN]) 511 break; 512 ino = idesc.id_number; 513 } 514 busy = 0; 515 if (ino != ROOTINO) 516 *--cp = '?'; 517 memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 518 } 519 520 void 521 catch(sig) 522 int sig; 523 { 524 if (!doinglevel2) 525 ckfini(0); 526 exit(12); 527 } 528 529 /* 530 * When preening, allow a single quit to signal 531 * a special exit after filesystem checks complete 532 * so that reboot sequence may be interrupted. 533 */ 534 void 535 catchquit(sig) 536 int sig; 537 { 538 printf("returning to single-user after filesystem check\n"); 539 returntosingle = 1; 540 (void)signal(SIGQUIT, SIG_DFL); 541 } 542 543 /* 544 * Ignore a single quit signal; wait and flush just in case. 545 * Used by child processes in preen. 546 */ 547 void 548 voidquit(sig) 549 int sig; 550 { 551 552 sleep(1); 553 (void)signal(SIGQUIT, SIG_IGN); 554 (void)signal(SIGQUIT, SIG_DFL); 555 } 556 557 /* 558 * determine whether an inode should be fixed. 559 */ 560 int 561 dofix(idesc, msg) 562 register struct inodesc *idesc; 563 char *msg; 564 { 565 566 switch (idesc->id_fix) { 567 568 case DONTKNOW: 569 if (idesc->id_type == DATA) 570 direrror(idesc->id_number, msg); 571 else 572 pwarn(msg); 573 if (preen) { 574 printf(" (SALVAGED)\n"); 575 idesc->id_fix = FIX; 576 return (ALTERED); 577 } 578 if (reply("SALVAGE") == 0) { 579 idesc->id_fix = NOFIX; 580 return (0); 581 } 582 idesc->id_fix = FIX; 583 return (ALTERED); 584 585 case FIX: 586 return (ALTERED); 587 588 case NOFIX: 589 case IGNORE: 590 return (0); 591 592 default: 593 errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix); 594 } 595 /* NOTREACHED */ 596 return (0); 597 } 598 599 #if __STDC__ 600 #include <stdarg.h> 601 #else 602 #include <varargs.h> 603 #endif 604 605 /* 606 * An unexpected inconsistency occured. 607 * Die if preening or filesystem is running with soft dependency protocol, 608 * otherwise just print message and continue. 609 */ 610 void 611 #if __STDC__ 612 pfatal(const char *fmt, ...) 613 #else 614 pfatal(fmt, va_alist) 615 char *fmt; 616 va_dcl 617 #endif 618 { 619 va_list ap; 620 #if __STDC__ 621 va_start(ap, fmt); 622 #else 623 va_start(ap); 624 #endif 625 if (!preen) { 626 (void)vfprintf(stderr, fmt, ap); 627 va_end(ap); 628 if (usedsoftdep) 629 (void)fprintf(stderr, 630 "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n"); 631 return; 632 } 633 if (cdevname == NULL) 634 cdevname = "fsck"; 635 (void)fprintf(stderr, "%s: ", cdevname); 636 (void)vfprintf(stderr, fmt, ap); 637 (void)fprintf(stderr, 638 "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n", 639 cdevname, usedsoftdep ? " SOFT UPDATE " : " "); 640 ckfini(0); 641 exit(EEXIT); 642 } 643 644 /* 645 * Pwarn just prints a message when not preening or running soft dependency 646 * protocol, or a warning (preceded by filename) when preening. 647 */ 648 void 649 #if __STDC__ 650 pwarn(const char *fmt, ...) 651 #else 652 pwarn(fmt, va_alist) 653 char *fmt; 654 va_dcl 655 #endif 656 { 657 va_list ap; 658 #if __STDC__ 659 va_start(ap, fmt); 660 #else 661 va_start(ap); 662 #endif 663 if (preen) 664 (void)fprintf(stderr, "%s: ", cdevname); 665 (void)vfprintf(stderr, fmt, ap); 666 va_end(ap); 667 } 668 669 /* 670 * Stub for routines from kernel. 671 */ 672 void 673 #if __STDC__ 674 panic(const char *fmt, ...) 675 #else 676 panic(fmt, va_alist) 677 char *fmt; 678 va_dcl 679 #endif 680 { 681 va_list ap; 682 #if __STDC__ 683 va_start(ap, fmt); 684 #else 685 va_start(ap); 686 #endif 687 pfatal("INTERNAL INCONSISTENCY:"); 688 (void)vfprintf(stderr, fmt, ap); 689 va_end(ap); 690 exit(EEXIT); 691 } 692 693 char * 694 blockcheck(origname) 695 char *origname; 696 { 697 struct stat stslash, stblock, stchar; 698 char *newname, *raw; 699 struct fstab *fsinfo; 700 int retried = 0, len; 701 702 newname = origname; 703 retry: 704 if (stat(newname, &stblock) < 0) { 705 printf("Can't stat %s: %s\n", newname, strerror(errno)); 706 return (origname); 707 } 708 switch(stblock.st_mode & S_IFMT) { 709 case S_IFCHR: 710 case S_IFBLK: 711 return(newname); 712 case S_IFDIR: 713 if (retried) 714 break; 715 716 len = strlen(origname) - 1; 717 if (len > 0 && origname[len] == '/') 718 /* remove trailing slash */ 719 origname[len] = '\0'; 720 if ((fsinfo = getfsfile(origname)) == NULL) { 721 printf("Can't resolve %s to character special device", 722 origname); 723 return (0); 724 } 725 newname = fsinfo->fs_spec; 726 retried++; 727 goto retry; 728 } 729 /* 730 * Not a block or character device, just return name and 731 * let the user decide whether to use it. 732 */ 733 return (origname); 734 } 735