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