1 /* $NetBSD: utilities.c,v 1.16 1996/04/05 01:45:33 cgd Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)utilities.c 8.1 (Berkeley) 6/5/93"; 39 #else 40 static char rcsid[] = "$NetBSD: utilities.c,v 1.16 1996/04/05 01:45:33 cgd Exp $"; 41 #endif 42 #endif /* not lint */ 43 44 #include <sys/param.h> 45 #include <sys/time.h> 46 #include <ufs/ufs/dinode.h> 47 #include <ufs/ufs/dir.h> 48 #include <ufs/ffs/fs.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <ctype.h> 53 #include <unistd.h> 54 55 #include "fsck.h" 56 #include "extern.h" 57 58 long diskreads, totalreads; /* Disk cache statistics */ 59 60 int 61 ftypeok(dp) 62 struct dinode *dp; 63 { 64 switch (dp->di_mode & IFMT) { 65 66 case IFDIR: 67 case IFREG: 68 case IFBLK: 69 case IFCHR: 70 case IFLNK: 71 case IFSOCK: 72 case IFIFO: 73 return (1); 74 75 default: 76 if (debug) 77 printf("bad file type 0%o\n", dp->di_mode); 78 return (0); 79 } 80 } 81 82 int 83 reply(question) 84 char *question; 85 { 86 int persevere; 87 char c; 88 89 if (preen) 90 pfatal("INTERNAL ERROR: GOT TO reply()"); 91 persevere = !strcmp(question, "CONTINUE"); 92 printf("\n"); 93 if (!persevere && (nflag || fswritefd < 0)) { 94 printf("%s? no\n\n", question); 95 return (0); 96 } 97 if (yflag || (persevere && nflag)) { 98 printf("%s? yes\n\n", question); 99 return (1); 100 } 101 do { 102 printf("%s? [yn] ", question); 103 (void) fflush(stdout); 104 c = getc(stdin); 105 while (c != '\n' && getc(stdin) != '\n') 106 if (feof(stdin)) 107 return (0); 108 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 109 printf("\n"); 110 if (c == 'y' || c == 'Y') 111 return (1); 112 return (0); 113 } 114 115 /* 116 * Malloc buffers and set up cache. 117 */ 118 void 119 bufinit() 120 { 121 register struct bufarea *bp; 122 long bufcnt, i; 123 char *bufp; 124 125 pbp = pdirbp = (struct bufarea *)0; 126 bufp = malloc((unsigned int)sblock.fs_bsize); 127 if (bufp == 0) 128 errexit("cannot allocate buffer pool\n"); 129 cgblk.b_un.b_buf = bufp; 130 initbarea(&cgblk); 131 bufhead.b_next = bufhead.b_prev = &bufhead; 132 bufcnt = MAXBUFSPACE / sblock.fs_bsize; 133 if (bufcnt < MINBUFS) 134 bufcnt = MINBUFS; 135 for (i = 0; i < bufcnt; i++) { 136 bp = (struct bufarea *)malloc(sizeof(struct bufarea)); 137 bufp = malloc((unsigned int)sblock.fs_bsize); 138 if (bp == NULL || bufp == NULL) { 139 if (i >= MINBUFS) 140 break; 141 errexit("cannot allocate buffer pool\n"); 142 } 143 bp->b_un.b_buf = bufp; 144 bp->b_prev = &bufhead; 145 bp->b_next = bufhead.b_next; 146 bufhead.b_next->b_prev = bp; 147 bufhead.b_next = bp; 148 initbarea(bp); 149 } 150 bufhead.b_size = i; /* save number of buffers */ 151 } 152 153 /* 154 * Manage a cache of directory blocks. 155 */ 156 struct bufarea * 157 getdatablk(blkno, size) 158 daddr_t blkno; 159 long size; 160 { 161 register struct bufarea *bp; 162 163 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 164 if (bp->b_bno == fsbtodb(&sblock, blkno)) 165 goto foundit; 166 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 167 if ((bp->b_flags & B_INUSE) == 0) 168 break; 169 if (bp == &bufhead) 170 errexit("deadlocked buffer pool\n"); 171 getblk(bp, blkno, size); 172 /* fall through */ 173 foundit: 174 totalreads++; 175 bp->b_prev->b_next = bp->b_next; 176 bp->b_next->b_prev = bp->b_prev; 177 bp->b_prev = &bufhead; 178 bp->b_next = bufhead.b_next; 179 bufhead.b_next->b_prev = bp; 180 bufhead.b_next = bp; 181 bp->b_flags |= B_INUSE; 182 return (bp); 183 } 184 185 void 186 getblk(bp, blk, size) 187 register struct bufarea *bp; 188 daddr_t blk; 189 long size; 190 { 191 daddr_t dblk; 192 193 dblk = fsbtodb(&sblock, blk); 194 if (bp->b_bno != dblk) { 195 flush(fswritefd, bp); 196 diskreads++; 197 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 198 bp->b_bno = dblk; 199 bp->b_size = size; 200 } 201 } 202 203 void 204 flush(fd, bp) 205 int fd; 206 register struct bufarea *bp; 207 { 208 register int i, j; 209 210 if (!bp->b_dirty) 211 return; 212 if (bp->b_errs != 0) 213 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 214 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 215 bp->b_bno); 216 bp->b_dirty = 0; 217 bp->b_errs = 0; 218 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 219 if (bp != &sblk) 220 return; 221 for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 222 bwrite(fswritefd, (char *)sblock.fs_csp[j], 223 fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 224 sblock.fs_cssize - i < sblock.fs_bsize ? 225 sblock.fs_cssize - i : sblock.fs_bsize); 226 } 227 } 228 229 void 230 rwerror(mesg, blk) 231 char *mesg; 232 daddr_t blk; 233 { 234 235 if (preen == 0) 236 printf("\n"); 237 pfatal("CANNOT %s: BLK %ld", mesg, blk); 238 if (reply("CONTINUE") == 0) 239 errexit("Program terminated\n"); 240 } 241 242 void 243 ckfini(markclean) 244 int markclean; 245 { 246 register struct bufarea *bp, *nbp; 247 int cnt = 0; 248 249 if (fswritefd < 0) { 250 (void)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 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 271 pbp = pdirbp = (struct bufarea *)0; 272 if (markclean && (sblock.fs_clean & FS_ISCLEAN) == 0) { 273 /* 274 * Mark the file system as clean, and sync the superblock. 275 */ 276 if (preen) 277 pwarn("MARKING FILE SYSTEM CLEAN\n"); 278 else if (!reply("MARK FILE SYSTEM CLEAN")) 279 markclean = 0; 280 if (markclean) { 281 sblock.fs_clean = FS_ISCLEAN; 282 sbdirty(); 283 flush(fswritefd, &sblk); 284 } 285 } 286 if (debug) 287 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 288 totalreads, (int)(diskreads * 100 / totalreads)); 289 (void)close(fsreadfd); 290 (void)close(fswritefd); 291 } 292 293 int 294 bread(fd, buf, blk, size) 295 int fd; 296 char *buf; 297 daddr_t blk; 298 long size; 299 { 300 char *cp; 301 int i, errs; 302 off_t offset; 303 304 offset = blk; 305 offset *= dev_bsize; 306 if (lseek(fd, offset, 0) < 0) 307 rwerror("SEEK", blk); 308 else if (read(fd, buf, (int)size) == size) 309 return (0); 310 rwerror("READ", blk); 311 if (lseek(fd, offset, 0) < 0) 312 rwerror("SEEK", blk); 313 errs = 0; 314 memset(buf, 0, (size_t)size); 315 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 316 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 317 if (read(fd, cp, (int)secsize) != secsize) { 318 (void)lseek(fd, offset + i + secsize, 0); 319 if (secsize != dev_bsize && dev_bsize != 1) 320 printf(" %ld (%ld),", 321 (blk * dev_bsize + i) / secsize, 322 blk + i / dev_bsize); 323 else 324 printf(" %ld,", blk + i / dev_bsize); 325 errs++; 326 } 327 } 328 printf("\n"); 329 return (errs); 330 } 331 332 void 333 bwrite(fd, buf, blk, size) 334 int fd; 335 char *buf; 336 daddr_t blk; 337 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 rwerror("WRITE", blk); 354 if (lseek(fd, offset, 0) < 0) 355 rwerror("SEEK", blk); 356 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 357 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 358 if (write(fd, cp, (int)dev_bsize) != dev_bsize) { 359 (void)lseek(fd, offset + i + dev_bsize, 0); 360 printf(" %ld,", blk + i / dev_bsize); 361 } 362 printf("\n"); 363 return; 364 } 365 366 /* 367 * allocate a data block with the specified number of fragments 368 */ 369 int 370 allocblk(frags) 371 long frags; 372 { 373 register int i, j, k; 374 375 if (frags <= 0 || frags > sblock.fs_frag) 376 return (0); 377 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 378 for (j = 0; j <= sblock.fs_frag - frags; j++) { 379 if (testbmap(i + j)) 380 continue; 381 for (k = 1; k < frags; k++) 382 if (testbmap(i + j + k)) 383 break; 384 if (k < frags) { 385 j += k; 386 continue; 387 } 388 for (k = 0; k < frags; k++) 389 setbmap(i + j + k); 390 n_blks += frags; 391 return (i + j); 392 } 393 } 394 return (0); 395 } 396 397 /* 398 * Free a previously allocated block 399 */ 400 void 401 freeblk(blkno, frags) 402 daddr_t blkno; 403 long frags; 404 { 405 struct inodesc idesc; 406 407 idesc.id_blkno = blkno; 408 idesc.id_numfrags = frags; 409 (void)pass4check(&idesc); 410 } 411 412 /* 413 * Find a pathname 414 */ 415 void 416 getpathname(namebuf, curdir, ino) 417 char *namebuf; 418 ino_t curdir, ino; 419 { 420 int len; 421 register char *cp; 422 struct inodesc idesc; 423 static int busy = 0; 424 425 if (curdir == ino && ino == ROOTINO) { 426 (void)strcpy(namebuf, "/"); 427 return; 428 } 429 if (busy || 430 (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { 431 (void)strcpy(namebuf, "?"); 432 return; 433 } 434 busy = 1; 435 memset(&idesc, 0, sizeof(struct inodesc)); 436 idesc.id_type = DATA; 437 idesc.id_fix = IGNORE; 438 cp = &namebuf[MAXPATHLEN - 1]; 439 *cp = '\0'; 440 if (curdir != ino) { 441 idesc.id_parent = curdir; 442 goto namelookup; 443 } 444 while (ino != ROOTINO) { 445 idesc.id_number = ino; 446 idesc.id_func = findino; 447 idesc.id_name = ".."; 448 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 449 break; 450 namelookup: 451 idesc.id_number = idesc.id_parent; 452 idesc.id_parent = ino; 453 idesc.id_func = findname; 454 idesc.id_name = namebuf; 455 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 456 break; 457 len = strlen(namebuf); 458 cp -= len; 459 memcpy(cp, namebuf, (size_t)len); 460 *--cp = '/'; 461 if (cp < &namebuf[MAXNAMLEN]) 462 break; 463 ino = idesc.id_number; 464 } 465 busy = 0; 466 if (ino != ROOTINO) 467 *--cp = '?'; 468 memcpy(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 469 } 470 471 void 472 catch() 473 { 474 if (!doinglevel2) 475 ckfini(0); 476 exit(12); 477 } 478 479 /* 480 * When preening, allow a single quit to signal 481 * a special exit after filesystem checks complete 482 * so that reboot sequence may be interrupted. 483 */ 484 void 485 catchquit() 486 { 487 extern returntosingle; 488 489 printf("returning to single-user after filesystem check\n"); 490 returntosingle = 1; 491 (void)signal(SIGQUIT, SIG_DFL); 492 } 493 494 /* 495 * Ignore a single quit signal; wait and flush just in case. 496 * Used by child processes in preen. 497 */ 498 void 499 voidquit() 500 { 501 502 sleep(1); 503 (void)signal(SIGQUIT, SIG_IGN); 504 (void)signal(SIGQUIT, SIG_DFL); 505 } 506 507 /* 508 * determine whether an inode should be fixed. 509 */ 510 int 511 dofix(idesc, msg) 512 register struct inodesc *idesc; 513 char *msg; 514 { 515 516 switch (idesc->id_fix) { 517 518 case DONTKNOW: 519 if (idesc->id_type == DATA) 520 direrror(idesc->id_number, msg); 521 else 522 pwarn(msg); 523 if (preen) { 524 printf(" (SALVAGED)\n"); 525 idesc->id_fix = FIX; 526 return (ALTERED); 527 } 528 if (reply("SALVAGE") == 0) { 529 idesc->id_fix = NOFIX; 530 return (0); 531 } 532 idesc->id_fix = FIX; 533 return (ALTERED); 534 535 case FIX: 536 return (ALTERED); 537 538 case NOFIX: 539 case IGNORE: 540 return (0); 541 542 default: 543 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 544 } 545 /* NOTREACHED */ 546 } 547 548 /* VARARGS1 */ 549 errexit(s1, s2, s3, s4) 550 char *s1; 551 long s2, s3, s4; 552 { 553 printf(s1, s2, s3, s4); 554 exit(8); 555 } 556 557 /* 558 * An unexpected inconsistency occured. 559 * Die if preening, otherwise just print message and continue. 560 */ 561 /* VARARGS1 */ 562 pfatal(s, a1, a2, a3) 563 char *s; 564 long a1, a2, a3; 565 { 566 567 if (preen) { 568 printf("%s: ", cdevname); 569 printf(s, a1, a2, a3); 570 printf("\n"); 571 printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck_ffs MANUALLY.\n", 572 cdevname); 573 exit(8); 574 } 575 printf(s, a1, a2, a3); 576 } 577 578 /* 579 * Pwarn just prints a message when not preening, 580 * or a warning (preceded by filename) when preening. 581 */ 582 /* VARARGS1 */ 583 pwarn(s, a1, a2, a3, a4, a5, a6) 584 char *s; 585 long a1, a2, a3, a4, a5, a6; 586 { 587 588 if (preen) 589 printf("%s: ", cdevname); 590 printf(s, a1, a2, a3, a4, a5, a6); 591 } 592 593 #ifndef lint 594 /* 595 * Stub for routines from kernel. 596 */ 597 panic(s) 598 char *s; 599 { 600 601 pfatal("INTERNAL INCONSISTENCY:"); 602 errexit(s); 603 } 604 #endif 605