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