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. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#)utilities.c 8.6 (Berkeley) 5/19/95 30 * $FreeBSD: src/sbin/fsck/utilities.c,v 1.11.2.3 2001/01/23 23:11:07 iedowse Exp $ 31 */ 32 33 #include <sys/param.h> 34 35 #include <vfs/ufs/dinode.h> 36 #include <vfs/ufs/dir.h> 37 #include <vfs/ufs/fs.h> 38 39 #include <err.h> 40 #include <string.h> 41 42 #include "fsck.h" 43 44 long diskreads, totalreads; /* Disk cache statistics */ 45 46 static void rwerror(char *mesg, ufs_daddr_t blk); 47 48 int 49 ftypeok(struct ufs1_dinode *dp) 50 { 51 switch (dp->di_mode & IFMT) { 52 53 case IFDIR: 54 case IFREG: 55 case IFBLK: 56 case IFCHR: 57 case IFLNK: 58 case IFSOCK: 59 case IFIFO: 60 return (1); 61 62 default: 63 if (debug) 64 printf("bad file type 0%o\n", dp->di_mode); 65 return (0); 66 } 67 } 68 69 int 70 reply(char *question) 71 { 72 int persevere; 73 char c; 74 75 if (preen) 76 pfatal("INTERNAL ERROR: GOT TO reply()"); 77 persevere = !strcmp(question, "CONTINUE"); 78 printf("\n"); 79 if (!persevere && (nflag || fswritefd < 0)) { 80 printf("%s? no\n\n", question); 81 resolved = 0; 82 return (0); 83 } 84 if (yflag || (persevere && nflag)) { 85 printf("%s? yes\n\n", question); 86 return (1); 87 } 88 do { 89 printf("%s? [yn] ", question); 90 fflush(stdout); 91 c = getc(stdin); 92 while (c != '\n' && getc(stdin) != '\n') { 93 if (feof(stdin)) { 94 resolved = 0; 95 return (0); 96 } 97 } 98 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 99 printf("\n"); 100 if (c == 'y' || c == 'Y') 101 return (1); 102 resolved = 0; 103 return (0); 104 } 105 106 /* 107 * Look up state information for an inode. 108 */ 109 struct inostat * 110 inoinfo(ufs1_ino_t inum) 111 { 112 static struct inostat unallocated = { USTATE, 0, 0 }; 113 struct inostatlist *ilp; 114 int iloff; 115 116 if (inum > maxino) 117 errx(EEXIT, "inoinfo: inumber %d out of range", inum); 118 ilp = &inostathead[inum / sblock.fs_ipg]; 119 iloff = inum % sblock.fs_ipg; 120 if (iloff >= ilp->il_numalloced) 121 return (&unallocated); 122 return (&ilp->il_stat[iloff]); 123 } 124 125 /* 126 * Malloc buffers and set up cache. 127 */ 128 void 129 bufinit(void) 130 { 131 struct bufarea *bp; 132 long bufcnt, i; 133 char *bufp; 134 135 pbp = pdirbp = NULL; 136 bufp = malloc((unsigned int)sblock.fs_bsize); 137 if (bufp == NULL) 138 errx(EEXIT, "cannot allocate buffer pool"); 139 cgblk.b_un.b_buf = bufp; 140 initbarea(&cgblk); 141 bufhead.b_next = bufhead.b_prev = &bufhead; 142 bufcnt = MAXBUFSPACE / sblock.fs_bsize; 143 if (bufcnt < MINBUFS) 144 bufcnt = MINBUFS; 145 for (i = 0; i < bufcnt; i++) { 146 bp = (struct bufarea *)malloc(sizeof(struct bufarea)); 147 bufp = malloc((unsigned int)sblock.fs_bsize); 148 if (bp == NULL || bufp == NULL) { 149 if (i >= MINBUFS) 150 break; 151 errx(EEXIT, "cannot allocate buffer pool"); 152 } 153 bp->b_un.b_buf = bufp; 154 bp->b_prev = &bufhead; 155 bp->b_next = bufhead.b_next; 156 bufhead.b_next->b_prev = bp; 157 bufhead.b_next = bp; 158 initbarea(bp); 159 } 160 bufhead.b_size = i; /* save number of buffers */ 161 } 162 163 /* 164 * Manage a cache of directory blocks. 165 */ 166 struct bufarea * 167 getdatablk(ufs_daddr_t blkno, long size) 168 { 169 struct bufarea *bp; 170 171 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 172 if (bp->b_bno == fsbtodb(&sblock, blkno)) 173 goto foundit; 174 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 175 if ((bp->b_flags & B_INUSE) == 0) 176 break; 177 if (bp == &bufhead) 178 errx(EEXIT, "deadlocked buffer pool"); 179 getblk(bp, blkno, size); 180 /* fall through */ 181 foundit: 182 totalreads++; 183 bp->b_prev->b_next = bp->b_next; 184 bp->b_next->b_prev = bp->b_prev; 185 bp->b_prev = &bufhead; 186 bp->b_next = bufhead.b_next; 187 bufhead.b_next->b_prev = bp; 188 bufhead.b_next = bp; 189 bp->b_flags |= B_INUSE; 190 return (bp); 191 } 192 193 void 194 getblk(struct bufarea *bp, ufs_daddr_t blk, long size) 195 { 196 ufs_daddr_t dblk; 197 198 dblk = fsbtodb(&sblock, blk); 199 if (bp->b_bno != dblk) { 200 flush(fswritefd, bp); 201 diskreads++; 202 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 203 bp->b_bno = dblk; 204 bp->b_size = size; 205 } 206 } 207 208 void 209 flush(int fd, struct bufarea *bp) 210 { 211 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 + i, 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 232 static void 233 rwerror(char *mesg, ufs_daddr_t blk) 234 { 235 236 if (preen == 0) 237 printf("\n"); 238 pfatal("CANNOT %s: BLK %d", mesg, blk); 239 if (reply("CONTINUE") == 0) 240 exit(EEXIT); 241 } 242 243 void 244 ckfini(int markclean) 245 { 246 struct bufarea *bp, *nbp; 247 int ofsmodified, cnt = 0; 248 249 if (fswritefd < 0) { 250 close(fsreadfd); 251 return; 252 } 253 flush(fswritefd, &sblk); 254 if (havesb && sblk.b_bno != SBOFF / dev_bsize && 255 !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 256 sblk.b_bno = SBOFF / dev_bsize; 257 sbdirty(); 258 flush(fswritefd, &sblk); 259 } 260 flush(fswritefd, &cgblk); 261 free(cgblk.b_un.b_buf); 262 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 263 cnt++; 264 flush(fswritefd, bp); 265 nbp = bp->b_prev; 266 free(bp->b_un.b_buf); 267 free((char *)bp); 268 } 269 if (bufhead.b_size != cnt) 270 errx(EEXIT, "panic: lost %d buffers", bufhead.b_size - cnt); 271 pbp = pdirbp = NULL; 272 if (sblock.fs_clean != markclean) { 273 sblock.fs_clean = markclean; 274 sbdirty(); 275 ofsmodified = fsmodified; 276 flush(fswritefd, &sblk); 277 fsmodified = ofsmodified; 278 if (!preen) { 279 printf("\n***** FILE SYSTEM MARKED %s *****\n", 280 markclean ? "CLEAN" : "DIRTY"); 281 if (!markclean) 282 rerun = 1; 283 } 284 } else if (!preen && !markclean) { 285 printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); 286 rerun = 1; 287 } 288 if (debug) 289 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 290 totalreads, (int)(diskreads * 100 / totalreads)); 291 close(fsreadfd); 292 close(fswritefd); 293 } 294 295 int 296 bread(int fd, char *buf, ufs_daddr_t blk, 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 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 if (errs) 328 resolved = 0; 329 return (errs); 330 } 331 332 void 333 bwrite(int fd, char *buf, ufs_daddr_t blk, long size) 334 { 335 int i; 336 char *cp; 337 off_t offset; 338 339 if (fd < 0) 340 return; 341 offset = blk; 342 offset *= dev_bsize; 343 if (lseek(fd, offset, 0) < 0) 344 rwerror("SEEK", blk); 345 else if (write(fd, buf, (int)size) == size) { 346 fsmodified = 1; 347 return; 348 } 349 resolved = 0; 350 rwerror("WRITE", blk); 351 if (lseek(fd, offset, 0) < 0) 352 rwerror("SEEK", blk); 353 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 354 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 355 if (write(fd, cp, (int)dev_bsize) != dev_bsize) { 356 lseek(fd, offset + i + dev_bsize, 0); 357 printf(" %ld,", blk + i / dev_bsize); 358 } 359 printf("\n"); 360 return; 361 } 362 363 /* 364 * allocate a data block with the specified number of fragments 365 */ 366 ufs_daddr_t 367 allocblk(long frags) 368 { 369 int i, j, k, cg, baseblk; 370 struct cg *cgp = &cgrp; 371 372 if (frags <= 0 || frags > sblock.fs_frag) 373 return (0); 374 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 375 for (j = 0; j <= sblock.fs_frag - frags; j++) { 376 if (testbmap(i + j)) 377 continue; 378 for (k = 1; k < frags; k++) 379 if (testbmap(i + j + k)) 380 break; 381 if (k < frags) { 382 j += k; 383 continue; 384 } 385 cg = dtog(&sblock, i + j); 386 getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 387 if (!cg_chkmagic(cgp)) 388 pfatal("CG %d: BAD MAGIC NUMBER\n", cg); 389 baseblk = dtogd(&sblock, i + j); 390 for (k = 0; k < frags; k++) { 391 setbmap(i + j + k); 392 clrbit(cg_blksfree(cgp), baseblk + k); 393 } 394 n_blks += frags; 395 if (frags == sblock.fs_frag) 396 cgp->cg_cs.cs_nbfree--; 397 else 398 cgp->cg_cs.cs_nffree -= frags; 399 cgdirty(); 400 return (i + j); 401 } 402 } 403 return (0); 404 } 405 406 /* 407 * Free a previously allocated block 408 */ 409 void 410 freeblk(ufs_daddr_t blkno, long frags) 411 { 412 struct inodesc idesc; 413 414 idesc.id_blkno = blkno; 415 idesc.id_numfrags = frags; 416 pass4check(&idesc); 417 } 418 419 /* 420 * Find a pathname 421 */ 422 void 423 getpathname(char *namebuf, ufs1_ino_t curdir, ufs1_ino_t ino) 424 { 425 int len; 426 char *cp; 427 struct inodesc idesc; 428 static int busy = 0; 429 430 if (curdir == ino && ino == ROOTINO) { 431 strcpy(namebuf, "/"); 432 return; 433 } 434 if (busy || 435 (inoinfo(curdir)->ino_state != DSTATE && 436 inoinfo(curdir)->ino_state != DFOUND)) { 437 strcpy(namebuf, "?"); 438 return; 439 } 440 busy = 1; 441 memset(&idesc, 0, sizeof(struct inodesc)); 442 idesc.id_type = DATA; 443 idesc.id_fix = IGNORE; 444 cp = &namebuf[MAXPATHLEN - 1]; 445 *cp = '\0'; 446 if (curdir != ino) { 447 idesc.id_parent = curdir; 448 goto namelookup; 449 } 450 while (ino != ROOTINO) { 451 idesc.id_number = ino; 452 idesc.id_func = findino; 453 idesc.id_name = ".."; 454 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 455 break; 456 namelookup: 457 idesc.id_number = idesc.id_parent; 458 idesc.id_parent = ino; 459 idesc.id_func = findname; 460 idesc.id_name = namebuf; 461 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 462 break; 463 len = strlen(namebuf); 464 cp -= len; 465 memmove(cp, namebuf, (size_t)len); 466 *--cp = '/'; 467 if (cp < &namebuf[MAXNAMLEN]) 468 break; 469 ino = idesc.id_number; 470 } 471 busy = 0; 472 if (ino != ROOTINO) 473 *--cp = '?'; 474 memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 475 } 476 477 /* ARGSUSED */ 478 void 479 catch(int sig) 480 { 481 if (!doinglevel2) 482 ckfini(0); 483 exit(12); 484 } 485 486 /* 487 * When preening, allow a single quit to signal 488 * a special exit after filesystem checks complete 489 * so that reboot sequence may be interrupted. 490 */ 491 /* ARGSUSED */ 492 void 493 catchquit(int sig) 494 { 495 printf("returning to single-user after filesystem check\n"); 496 returntosingle = 1; 497 signal(SIGQUIT, SIG_DFL); 498 } 499 500 /* 501 * Ignore a single quit signal; wait and flush just in case. 502 * Used by child processes in preen. 503 */ 504 /* ARGSUSED */ 505 void 506 voidquit(int sig) 507 { 508 509 sleep(1); 510 signal(SIGQUIT, SIG_IGN); 511 signal(SIGQUIT, SIG_DFL); 512 } 513 514 /* ARGSUSED */ 515 void 516 infohandler(int sig) 517 { 518 got_siginfo = 1; 519 } 520 521 /* 522 * determine whether an inode should be fixed. 523 */ 524 int 525 dofix(struct inodesc *idesc, 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("%s", 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 #include <stdarg.h> 562 563 /* 564 * An unexpected inconsistency occured. 565 * Die if preening or filesystem is running with soft dependency protocol, 566 * otherwise just print message and continue. 567 */ 568 void 569 pfatal(const char *fmt, ...) 570 { 571 va_list ap; 572 va_start(ap, fmt); 573 if (!preen) { 574 vfprintf(stderr, fmt, ap); 575 va_end(ap); 576 if (usedsoftdep) 577 fprintf(stderr, 578 "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n"); 579 return; 580 } 581 if (cdevname == NULL) 582 cdevname = "fsck"; 583 fprintf(stderr, "%s: ", cdevname); 584 vfprintf(stderr, fmt, ap); 585 fprintf(stderr, 586 "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n", 587 cdevname, usedsoftdep ? " SOFT UPDATE " : " "); 588 ckfini(0); 589 exit(EEXIT); 590 } 591 592 /* 593 * Pwarn just prints a message when not preening or running soft dependency 594 * protocol, or a warning (preceded by filename) when preening. 595 */ 596 void 597 pwarn(const char *fmt, ...) 598 { 599 va_list ap; 600 va_start(ap, fmt); 601 if (preen) 602 fprintf(stderr, "%s: ", cdevname); 603 vfprintf(stderr, fmt, ap); 604 va_end(ap); 605 } 606 607 /* 608 * Stub for routines from kernel. 609 */ 610 void 611 panic(const char *fmt, ...) 612 { 613 va_list ap; 614 615 va_start(ap, fmt); 616 pfatal("INTERNAL INCONSISTENCY:"); 617 vfprintf(stderr, fmt, ap); 618 va_end(ap); 619 exit(EEXIT); 620 } 621