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