utilities.c revision 41477
1198892Srdivacky/* 2198892Srdivacky * Copyright (c) 1980, 1986, 1993 3198892Srdivacky * The Regents of the University of California. All rights reserved. 4198892Srdivacky * 5198892Srdivacky * Redistribution and use in source and binary forms, with or without 6198892Srdivacky * modification, are permitted provided that the following conditions 7198892Srdivacky * are met: 8198892Srdivacky * 1. Redistributions of source code must retain the above copyright 9198892Srdivacky * notice, this list of conditions and the following disclaimer. 10198892Srdivacky * 2. Redistributions in binary form must reproduce the above copyright 11198892Srdivacky * notice, this list of conditions and the following disclaimer in the 12239462Sdim * documentation and/or other materials provided with the distribution. 13198892Srdivacky * 3. All advertising materials mentioning features or use of this software 14239462Sdim * must display the following acknowledgement: 15198892Srdivacky * This product includes software developed by the University of 16198892Srdivacky * California, Berkeley and its contributors. 17198892Srdivacky * 4. Neither the name of the University nor the names of its contributors 18198892Srdivacky * may be used to endorse or promote products derived from this software 19239462Sdim * without specific prior written permission. 20198892Srdivacky * 21198892Srdivacky * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22198892Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23198892Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24239462Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25239462Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26239462Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27239462Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28239462Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29239462Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30239462Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31239462Sdim * SUCH DAMAGE. 32239462Sdim */ 33239462Sdim 34239462Sdim#ifndef lint 35239462Sdim#if 0 36239462Sdimstatic const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; 37239462Sdim#endif 38239462Sdimstatic const char rcsid[] = 39239462Sdim "$Id: utilities.c,v 1.8 1998/06/15 07:07:21 charnier Exp $"; 40239462Sdim#endif /* not lint */ 41239462Sdim 42239462Sdim#include <sys/param.h> 43239462Sdim 44239462Sdim#include <ufs/ufs/dinode.h> 45239462Sdim#include <ufs/ufs/dir.h> 46239462Sdim#include <ufs/ffs/fs.h> 47239462Sdim 48239462Sdim#include <err.h> 49239462Sdim#include <string.h> 50198892Srdivacky 51234353Sdim#include "fsck.h" 52234353Sdim 53218893Sdimlong diskreads, totalreads; /* Disk cache statistics */ 54218893Sdim 55218893Sdimstatic void rwerror __P((char *mesg, ufs_daddr_t blk)); 56218893Sdim 57198892Srdivackyint 58198892Srdivackyftypeok(dp) 59198892Srdivacky struct dinode *dp; 60198892Srdivacky{ 61198892Srdivacky switch (dp->di_mode & IFMT) { 62198892Srdivacky 63198892Srdivacky case IFDIR: 64239462Sdim case IFREG: 65239462Sdim case IFBLK: 66239462Sdim case IFCHR: 67239462Sdim case IFLNK: 68239462Sdim case IFSOCK: 69239462Sdim case IFIFO: 70239462Sdim return (1); 71239462Sdim 72210299Sed default: 73239462Sdim if (debug) 74198892Srdivacky printf("bad file type 0%o\n", dp->di_mode); 75198892Srdivacky return (0); 76239462Sdim } 77239462Sdim} 78239462Sdim 79198892Srdivackyint 80239462Sdimreply(question) 81263508Sdim char *question; 82239462Sdim{ 83239462Sdim int persevere; 84239462Sdim char c; 85239462Sdim 86239462Sdim if (preen) 87239462Sdim pfatal("INTERNAL ERROR: GOT TO reply()"); 88239462Sdim persevere = !strcmp(question, "CONTINUE"); 89239462Sdim printf("\n"); 90239462Sdim if (!persevere && (nflag || fswritefd < 0)) { 91239462Sdim printf("%s? no\n\n", question); 92239462Sdim resolved = 0; 93239462Sdim return (0); 94239462Sdim } 95239462Sdim if (yflag || (persevere && nflag)) { 96239462Sdim printf("%s? yes\n\n", question); 97239462Sdim return (1); 98198892Srdivacky } 99239462Sdim do { 100239462Sdim printf("%s? [yn] ", question); 101239462Sdim (void) fflush(stdout); 102239462Sdim c = getc(stdin); 103239462Sdim while (c != '\n' && getc(stdin) != '\n') { 104239462Sdim if (feof(stdin)) { 105239462Sdim resolved = 0; 106239462Sdim return (0); 107198892Srdivacky } 108239462Sdim } 109239462Sdim } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 110239462Sdim printf("\n"); 111239462Sdim if (c == 'y' || c == 'Y') 112239462Sdim return (1); 113239462Sdim resolved = 0; 114239462Sdim return (0); 115239462Sdim} 116239462Sdim 117239462Sdim/* 118239462Sdim * Look up state information for an inode. 119239462Sdim */ 120198892Srdivackystruct inostat * 121239462Sdiminoinfo(inum) 122239462Sdim ino_t inum; 123239462Sdim{ 124239462Sdim static struct inostat unallocated = { USTATE, 0, 0 }; 125239462Sdim struct inostatlist *ilp; 126239462Sdim int iloff; 127226633Sdim 128239462Sdim if (inum > maxino) 129239462Sdim errx(EEXIT, "inoinfo: inumber %d out of range", inum); 130239462Sdim ilp = &inostathead[inum / sblock.fs_ipg]; 131239462Sdim iloff = inum % sblock.fs_ipg; 132239462Sdim if (iloff >= ilp->il_numalloced) 133239462Sdim return (&unallocated); 134198892Srdivacky return (&ilp->il_stat[iloff]); 135239462Sdim} 136239462Sdim 137239462Sdim/* 138198892Srdivacky * Malloc buffers and set up cache. 139239462Sdim */ 140243830Sdimvoid 141198892Srdivackybufinit() 142239462Sdim{ 143198892Srdivacky register struct bufarea *bp; 144239462Sdim long bufcnt, i; 145239462Sdim char *bufp; 146239462Sdim 147239462Sdim pbp = pdirbp = (struct bufarea *)0; 148239462Sdim bufp = malloc((unsigned int)sblock.fs_bsize); 149198892Srdivacky if (bufp == 0) 150239462Sdim errx(EEXIT, "cannot allocate buffer pool"); 151239462Sdim cgblk.b_un.b_buf = bufp; 152239462Sdim initbarea(&cgblk); 153239462Sdim bufhead.b_next = bufhead.b_prev = &bufhead; 154239462Sdim bufcnt = MAXBUFSPACE / sblock.fs_bsize; 155239462Sdim if (bufcnt < MINBUFS) 156239462Sdim bufcnt = MINBUFS; 157198892Srdivacky for (i = 0; i < bufcnt; i++) { 158239462Sdim bp = (struct bufarea *)malloc(sizeof(struct bufarea)); 159239462Sdim bufp = malloc((unsigned int)sblock.fs_bsize); 160198892Srdivacky if (bp == NULL || bufp == NULL) { 161239462Sdim if (i >= MINBUFS) 162239462Sdim break; 163239462Sdim errx(EEXIT, "cannot allocate buffer pool"); 164198892Srdivacky } 165239462Sdim bp->b_un.b_buf = bufp; 166239462Sdim bp->b_prev = &bufhead; 167239462Sdim bp->b_next = bufhead.b_next; 168198892Srdivacky bufhead.b_next->b_prev = bp; 169198892Srdivacky bufhead.b_next = bp; 170198892Srdivacky initbarea(bp); 171 } 172 bufhead.b_size = i; /* save number of buffers */ 173} 174 175/* 176 * Manage a cache of directory blocks. 177 */ 178struct bufarea * 179getdatablk(blkno, size) 180 ufs_daddr_t blkno; 181 long size; 182{ 183 register struct bufarea *bp; 184 185 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 186 if (bp->b_bno == fsbtodb(&sblock, blkno)) 187 goto foundit; 188 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 189 if ((bp->b_flags & B_INUSE) == 0) 190 break; 191 if (bp == &bufhead) 192 errx(EEXIT, "deadlocked buffer pool"); 193 getblk(bp, blkno, size); 194 /* fall through */ 195foundit: 196 totalreads++; 197 bp->b_prev->b_next = bp->b_next; 198 bp->b_next->b_prev = bp->b_prev; 199 bp->b_prev = &bufhead; 200 bp->b_next = bufhead.b_next; 201 bufhead.b_next->b_prev = bp; 202 bufhead.b_next = bp; 203 bp->b_flags |= B_INUSE; 204 return (bp); 205} 206 207void 208getblk(bp, blk, size) 209 register struct bufarea *bp; 210 ufs_daddr_t blk; 211 long size; 212{ 213 ufs_daddr_t dblk; 214 215 dblk = fsbtodb(&sblock, blk); 216 if (bp->b_bno != dblk) { 217 flush(fswritefd, bp); 218 diskreads++; 219 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 220 bp->b_bno = dblk; 221 bp->b_size = size; 222 } 223} 224 225void 226flush(fd, bp) 227 int fd; 228 register struct bufarea *bp; 229{ 230 register int i, j; 231 232 if (!bp->b_dirty) 233 return; 234 if (bp->b_errs != 0) 235 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 236 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 237 bp->b_bno); 238 bp->b_dirty = 0; 239 bp->b_errs = 0; 240 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 241 if (bp != &sblk) 242 return; 243 for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 244 bwrite(fswritefd, (char *)sblock.fs_csp[j], 245 fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 246 sblock.fs_cssize - i < sblock.fs_bsize ? 247 sblock.fs_cssize - i : sblock.fs_bsize); 248 } 249} 250 251static void 252rwerror(mesg, blk) 253 char *mesg; 254 ufs_daddr_t blk; 255{ 256 257 if (preen == 0) 258 printf("\n"); 259 pfatal("CANNOT %s: BLK %ld", mesg, blk); 260 if (reply("CONTINUE") == 0) 261 exit(EEXIT); 262} 263 264void 265ckfini(markclean) 266 int markclean; 267{ 268 register struct bufarea *bp, *nbp; 269 int ofsmodified, cnt = 0; 270 271 if (fswritefd < 0) { 272 (void)close(fsreadfd); 273 return; 274 } 275 flush(fswritefd, &sblk); 276 if (havesb && sblk.b_bno != SBOFF / dev_bsize && 277 !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 278 sblk.b_bno = SBOFF / dev_bsize; 279 sbdirty(); 280 flush(fswritefd, &sblk); 281 } 282 flush(fswritefd, &cgblk); 283 free(cgblk.b_un.b_buf); 284 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 285 cnt++; 286 flush(fswritefd, bp); 287 nbp = bp->b_prev; 288 free(bp->b_un.b_buf); 289 free((char *)bp); 290 } 291 if (bufhead.b_size != cnt) 292 errx(EEXIT, "panic: lost %d buffers", bufhead.b_size - cnt); 293 pbp = pdirbp = (struct bufarea *)0; 294 if (sblock.fs_clean != markclean) { 295 sblock.fs_clean = markclean; 296 sbdirty(); 297 ofsmodified = fsmodified; 298 flush(fswritefd, &sblk); 299 fsmodified = ofsmodified; 300 if (!preen) { 301 printf("\n***** FILE SYSTEM MARKED %s *****\n", 302 markclean ? "CLEAN" : "DIRTY"); 303 if (!markclean) 304 rerun = 1; 305 } 306 } else if (!preen && !markclean) { 307 printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); 308 rerun = 1; 309 } 310 if (debug) 311 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 312 totalreads, (int)(diskreads * 100 / totalreads)); 313 (void)close(fsreadfd); 314 (void)close(fswritefd); 315} 316 317int 318bread(fd, buf, blk, size) 319 int fd; 320 char *buf; 321 ufs_daddr_t blk; 322 long size; 323{ 324 char *cp; 325 int i, errs; 326 off_t offset; 327 328 offset = blk; 329 offset *= dev_bsize; 330 if (lseek(fd, offset, 0) < 0) 331 rwerror("SEEK", blk); 332 else if (read(fd, buf, (int)size) == size) 333 return (0); 334 rwerror("READ", blk); 335 if (lseek(fd, offset, 0) < 0) 336 rwerror("SEEK", blk); 337 errs = 0; 338 memset(buf, 0, (size_t)size); 339 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 340 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 341 if (read(fd, cp, (int)secsize) != secsize) { 342 (void)lseek(fd, offset + i + secsize, 0); 343 if (secsize != dev_bsize && dev_bsize != 1) 344 printf(" %ld (%ld),", 345 (blk * dev_bsize + i) / secsize, 346 blk + i / dev_bsize); 347 else 348 printf(" %ld,", blk + i / dev_bsize); 349 errs++; 350 } 351 } 352 printf("\n"); 353 if (errs) 354 resolved = 0; 355 return (errs); 356} 357 358void 359bwrite(fd, buf, blk, size) 360 int fd; 361 char *buf; 362 ufs_daddr_t blk; 363 long size; 364{ 365 int i; 366 char *cp; 367 off_t offset; 368 369 if (fd < 0) 370 return; 371 offset = blk; 372 offset *= dev_bsize; 373 if (lseek(fd, offset, 0) < 0) 374 rwerror("SEEK", blk); 375 else if (write(fd, buf, (int)size) == size) { 376 fsmodified = 1; 377 return; 378 } 379 resolved = 0; 380 rwerror("WRITE", blk); 381 if (lseek(fd, offset, 0) < 0) 382 rwerror("SEEK", blk); 383 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 384 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 385 if (write(fd, cp, (int)dev_bsize) != dev_bsize) { 386 (void)lseek(fd, offset + i + dev_bsize, 0); 387 printf(" %ld,", blk + i / dev_bsize); 388 } 389 printf("\n"); 390 return; 391} 392 393/* 394 * allocate a data block with the specified number of fragments 395 */ 396ufs_daddr_t 397allocblk(frags) 398 long frags; 399{ 400 int i, j, k, cg, baseblk; 401 struct cg *cgp = &cgrp; 402 403 if (frags <= 0 || frags > sblock.fs_frag) 404 return (0); 405 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 406 for (j = 0; j <= sblock.fs_frag - frags; j++) { 407 if (testbmap(i + j)) 408 continue; 409 for (k = 1; k < frags; k++) 410 if (testbmap(i + j + k)) 411 break; 412 if (k < frags) { 413 j += k; 414 continue; 415 } 416 cg = dtog(&sblock, i + j); 417 getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 418 if (!cg_chkmagic(cgp)) 419 pfatal("CG %d: BAD MAGIC NUMBER\n", cg); 420 baseblk = dtogd(&sblock, i + j); 421 for (k = 0; k < frags; k++) { 422 setbmap(i + j + k); 423 clrbit(cg_blksfree(cgp), baseblk + k); 424 } 425 n_blks += frags; 426 if (frags == sblock.fs_frag) 427 cgp->cg_cs.cs_nbfree--; 428 else 429 cgp->cg_cs.cs_nffree -= frags; 430 cgdirty(); 431 return (i + j); 432 } 433 } 434 return (0); 435} 436 437/* 438 * Free a previously allocated block 439 */ 440void 441freeblk(blkno, frags) 442 ufs_daddr_t blkno; 443 long frags; 444{ 445 struct inodesc idesc; 446 447 idesc.id_blkno = blkno; 448 idesc.id_numfrags = frags; 449 (void)pass4check(&idesc); 450} 451 452/* 453 * Find a pathname 454 */ 455void 456getpathname(namebuf, curdir, ino) 457 char *namebuf; 458 ino_t curdir, ino; 459{ 460 int len; 461 register char *cp; 462 struct inodesc idesc; 463 static int busy = 0; 464 465 if (curdir == ino && ino == ROOTINO) { 466 (void)strcpy(namebuf, "/"); 467 return; 468 } 469 if (busy || 470 (inoinfo(curdir)->ino_state != DSTATE && 471 inoinfo(curdir)->ino_state != DFOUND)) { 472 (void)strcpy(namebuf, "?"); 473 return; 474 } 475 busy = 1; 476 memset(&idesc, 0, sizeof(struct inodesc)); 477 idesc.id_type = DATA; 478 idesc.id_fix = IGNORE; 479 cp = &namebuf[MAXPATHLEN - 1]; 480 *cp = '\0'; 481 if (curdir != ino) { 482 idesc.id_parent = curdir; 483 goto namelookup; 484 } 485 while (ino != ROOTINO) { 486 idesc.id_number = ino; 487 idesc.id_func = findino; 488 idesc.id_name = ".."; 489 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 490 break; 491 namelookup: 492 idesc.id_number = idesc.id_parent; 493 idesc.id_parent = ino; 494 idesc.id_func = findname; 495 idesc.id_name = namebuf; 496 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 497 break; 498 len = strlen(namebuf); 499 cp -= len; 500 memmove(cp, namebuf, (size_t)len); 501 *--cp = '/'; 502 if (cp < &namebuf[MAXNAMLEN]) 503 break; 504 ino = idesc.id_number; 505 } 506 busy = 0; 507 if (ino != ROOTINO) 508 *--cp = '?'; 509 memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 510} 511 512void 513catch(sig) 514 int sig; 515{ 516 if (!doinglevel2) 517 ckfini(0); 518 exit(12); 519} 520 521/* 522 * When preening, allow a single quit to signal 523 * a special exit after filesystem checks complete 524 * so that reboot sequence may be interrupted. 525 */ 526void 527catchquit(sig) 528 int sig; 529{ 530 printf("returning to single-user after filesystem check\n"); 531 returntosingle = 1; 532 (void)signal(SIGQUIT, SIG_DFL); 533} 534 535/* 536 * Ignore a single quit signal; wait and flush just in case. 537 * Used by child processes in preen. 538 */ 539void 540voidquit(sig) 541 int sig; 542{ 543 544 sleep(1); 545 (void)signal(SIGQUIT, SIG_IGN); 546 (void)signal(SIGQUIT, SIG_DFL); 547} 548 549/* 550 * determine whether an inode should be fixed. 551 */ 552int 553dofix(idesc, msg) 554 register struct inodesc *idesc; 555 char *msg; 556{ 557 558 switch (idesc->id_fix) { 559 560 case DONTKNOW: 561 if (idesc->id_type == DATA) 562 direrror(idesc->id_number, msg); 563 else 564 pwarn(msg); 565 if (preen) { 566 printf(" (SALVAGED)\n"); 567 idesc->id_fix = FIX; 568 return (ALTERED); 569 } 570 if (reply("SALVAGE") == 0) { 571 idesc->id_fix = NOFIX; 572 return (0); 573 } 574 idesc->id_fix = FIX; 575 return (ALTERED); 576 577 case FIX: 578 return (ALTERED); 579 580 case NOFIX: 581 case IGNORE: 582 return (0); 583 584 default: 585 errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix); 586 } 587 /* NOTREACHED */ 588 return (0); 589} 590 591#if __STDC__ 592#include <stdarg.h> 593#else 594#include <varargs.h> 595#endif 596 597/* 598 * An unexpected inconsistency occured. 599 * Die if preening or filesystem is running with soft dependency protocol, 600 * otherwise just print message and continue. 601 */ 602void 603#if __STDC__ 604pfatal(const char *fmt, ...) 605#else 606pfatal(fmt, va_alist) 607 char *fmt; 608 va_dcl 609#endif 610{ 611 va_list ap; 612#if __STDC__ 613 va_start(ap, fmt); 614#else 615 va_start(ap); 616#endif 617 if (!preen) { 618 (void)vfprintf(stderr, fmt, ap); 619 va_end(ap); 620 if (usedsoftdep) 621 (void)fprintf(stderr, 622 "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n"); 623 return; 624 } 625 if (cdevname == NULL) 626 cdevname = "fsck"; 627 (void)fprintf(stderr, "%s: ", cdevname); 628 (void)vfprintf(stderr, fmt, ap); 629 (void)fprintf(stderr, 630 "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n", 631 cdevname, usedsoftdep ? " SOFT UPDATE " : " "); 632 ckfini(0); 633 exit(EEXIT); 634} 635 636/* 637 * Pwarn just prints a message when not preening or running soft dependency 638 * protocol, or a warning (preceded by filename) when preening. 639 */ 640void 641#if __STDC__ 642pwarn(const char *fmt, ...) 643#else 644pwarn(fmt, va_alist) 645 char *fmt; 646 va_dcl 647#endif 648{ 649 va_list ap; 650#if __STDC__ 651 va_start(ap, fmt); 652#else 653 va_start(ap); 654#endif 655 if (preen) 656 (void)fprintf(stderr, "%s: ", cdevname); 657 (void)vfprintf(stderr, fmt, ap); 658 va_end(ap); 659} 660 661/* 662 * Stub for routines from kernel. 663 */ 664void 665#if __STDC__ 666panic(const char *fmt, ...) 667#else 668panic(fmt, va_alist) 669 char *fmt; 670 va_dcl 671#endif 672{ 673 va_list ap; 674#if __STDC__ 675 va_start(ap, fmt); 676#else 677 va_start(ap); 678#endif 679 pfatal("INTERNAL INCONSISTENCY:"); 680 (void)vfprintf(stderr, fmt, ap); 681 va_end(ap); 682 exit(EEXIT); 683} 684