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