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