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