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