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