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