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.12 (Berkeley) 05/25/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 == 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 if (bp->b_bno == blk) 166 return (bp); 167 flush(fcp, bp); 168 diskreads++; 169 bp->b_errs = bread(fcp, bp->b_un.b_buf, fsbtodb(&sblock, blk), size); 170 bp->b_bno = blk; 171 bp->b_size = size; 172 return (bp); 173 } 174 175 flush(fcp, bp) 176 struct filecntl *fcp; 177 register BUFAREA *bp; 178 { 179 register int i, j; 180 181 if (!bp->b_dirty) 182 return; 183 if (bp->b_errs != 0) 184 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 185 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 186 bp->b_bno); 187 bp->b_dirty = 0; 188 bp->b_errs = 0; 189 bwrite(fcp, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 190 if (bp != &sblk) 191 return; 192 for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 193 bwrite(&dfile, (char *)sblock.fs_csp[j], 194 fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 195 sblock.fs_cssize - i < sblock.fs_bsize ? 196 sblock.fs_cssize - i : sblock.fs_bsize); 197 } 198 } 199 200 rwerr(s, blk) 201 char *s; 202 daddr_t blk; 203 { 204 205 if (preen == 0) 206 printf("\n"); 207 pfatal("CANNOT %s: BLK %ld", s, blk); 208 if (reply("CONTINUE") == 0) 209 errexit("Program terminated\n"); 210 } 211 212 ckfini() 213 { 214 register BUFAREA *bp; 215 int cnt = 0; 216 217 flush(&dfile, &sblk); 218 if (havesb && sblk.b_bno != SBOFF / dev_bsize && 219 !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 220 sblk.b_bno = SBOFF / dev_bsize; 221 sbdirty(); 222 flush(&dfile, &sblk); 223 } 224 flush(&dfile, &cgblk); 225 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) { 226 cnt++; 227 flush(&dfile, bp); 228 } 229 if (bufhead.b_size != cnt) 230 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 231 if (debug) 232 printf("cache missed %d of %d (%d%%)\n", diskreads, 233 totalreads, diskreads * 100 / totalreads); 234 (void)close(dfile.rfdes); 235 (void)close(dfile.wfdes); 236 } 237 238 bread(fcp, buf, blk, size) 239 register struct filecntl *fcp; 240 char *buf; 241 daddr_t blk; 242 long size; 243 { 244 char *cp; 245 int i, errs; 246 247 if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0) 248 rwerr("SEEK", blk); 249 else if (read(fcp->rfdes, buf, (int)size) == size) 250 return (0); 251 rwerr("READ", blk); 252 if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0) 253 rwerr("SEEK", blk); 254 errs = 0; 255 bzero(buf, size); 256 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 257 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 258 if (read(fcp->rfdes, cp, secsize) < 0) { 259 lseek(fcp->rfdes, blk * dev_bsize + i + secsize, 0); 260 if (secsize != dev_bsize && dev_bsize != 1) 261 printf(" %d (%d),", 262 (blk * dev_bsize + i) / secsize, 263 blk + i / dev_bsize); 264 else 265 printf(" %d,", blk + i / dev_bsize); 266 errs++; 267 } 268 } 269 printf("\n"); 270 return (errs); 271 } 272 273 bwrite(fcp, buf, blk, size) 274 register struct filecntl *fcp; 275 char *buf; 276 daddr_t blk; 277 long size; 278 { 279 int i; 280 char *cp; 281 282 if (fcp->wfdes < 0) 283 return; 284 if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0) 285 rwerr("SEEK", blk); 286 else if (write(fcp->wfdes, buf, (int)size) == size) { 287 fcp->mod = 1; 288 return; 289 } 290 rwerr("WRITE", blk); 291 if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0) 292 rwerr("SEEK", blk); 293 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 294 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 295 if (write(fcp->wfdes, cp, dev_bsize) < 0) { 296 lseek(fcp->rfdes, blk * dev_bsize + i + dev_bsize, 0); 297 printf(" %d,", blk + i / dev_bsize); 298 } 299 printf("\n"); 300 return; 301 } 302 303 /* 304 * allocate a data block with the specified number of fragments 305 */ 306 allocblk(frags) 307 int frags; 308 { 309 register int i, j, k; 310 311 if (frags <= 0 || frags > sblock.fs_frag) 312 return (0); 313 for (i = 0; i < fmax - sblock.fs_frag; i += sblock.fs_frag) { 314 for (j = 0; j <= sblock.fs_frag - frags; j++) { 315 if (getbmap(i + j)) 316 continue; 317 for (k = 1; k < frags; k++) 318 if (getbmap(i + j + k)) 319 break; 320 if (k < frags) { 321 j += k; 322 continue; 323 } 324 for (k = 0; k < frags; k++) 325 setbmap(i + j + k); 326 n_blks += frags; 327 return (i + j); 328 } 329 } 330 return (0); 331 } 332 333 /* 334 * Free a previously allocated block 335 */ 336 freeblk(blkno, frags) 337 daddr_t blkno; 338 int frags; 339 { 340 struct inodesc idesc; 341 342 idesc.id_blkno = blkno; 343 idesc.id_numfrags = frags; 344 pass4check(&idesc); 345 } 346 347 /* 348 * Find a pathname 349 */ 350 getpathname(namebuf, curdir, ino) 351 char *namebuf; 352 ino_t curdir, ino; 353 { 354 int len; 355 register char *cp; 356 struct inodesc idesc; 357 extern int findname(); 358 359 if (statemap[ino] != DSTATE && statemap[ino] != DFOUND) { 360 strcpy(namebuf, "?"); 361 return; 362 } 363 bzero(&idesc, sizeof(struct inodesc)); 364 idesc.id_type = DATA; 365 cp = &namebuf[BUFSIZ - 1]; 366 *cp = '\0'; 367 if (curdir != ino) { 368 idesc.id_parent = curdir; 369 goto namelookup; 370 } 371 while (ino != ROOTINO) { 372 idesc.id_number = ino; 373 idesc.id_func = findino; 374 idesc.id_name = ".."; 375 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 376 break; 377 namelookup: 378 idesc.id_number = idesc.id_parent; 379 idesc.id_parent = ino; 380 idesc.id_func = findname; 381 idesc.id_name = namebuf; 382 if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0) 383 break; 384 len = strlen(namebuf); 385 cp -= len; 386 if (cp < &namebuf[MAXNAMLEN]) 387 break; 388 bcopy(namebuf, cp, len); 389 *--cp = '/'; 390 ino = idesc.id_number; 391 } 392 if (ino != ROOTINO) { 393 strcpy(namebuf, "?"); 394 return; 395 } 396 bcopy(cp, namebuf, &namebuf[BUFSIZ] - cp); 397 } 398 399 catch() 400 { 401 402 ckfini(); 403 exit(12); 404 } 405 406 /* 407 * When preening, allow a single quit to signal 408 * a special exit after filesystem checks complete 409 * so that reboot sequence may be interrupted. 410 */ 411 catchquit() 412 { 413 extern returntosingle; 414 415 printf("returning to single-user after filesystem check\n"); 416 returntosingle = 1; 417 (void)signal(SIGQUIT, SIG_DFL); 418 } 419 420 /* 421 * Ignore a single quit signal; wait and flush just in case. 422 * Used by child processes in preen. 423 */ 424 voidquit() 425 { 426 427 sleep(1); 428 (void)signal(SIGQUIT, SIG_IGN); 429 (void)signal(SIGQUIT, SIG_DFL); 430 } 431 432 /* 433 * determine whether an inode should be fixed. 434 */ 435 dofix(idesc, msg) 436 register struct inodesc *idesc; 437 char *msg; 438 { 439 440 switch (idesc->id_fix) { 441 442 case DONTKNOW: 443 if (idesc->id_type == DATA) 444 direrr(idesc->id_number, msg); 445 else 446 pwarn(msg); 447 if (preen) { 448 printf(" (SALVAGED)\n"); 449 idesc->id_fix = FIX; 450 return (ALTERED); 451 } 452 if (reply("SALVAGE") == 0) { 453 idesc->id_fix = NOFIX; 454 return (0); 455 } 456 idesc->id_fix = FIX; 457 return (ALTERED); 458 459 case FIX: 460 return (ALTERED); 461 462 case NOFIX: 463 return (0); 464 465 default: 466 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 467 } 468 /* NOTREACHED */ 469 } 470 471 /* VARARGS1 */ 472 errexit(s1, s2, s3, s4) 473 char *s1; 474 { 475 printf(s1, s2, s3, s4); 476 exit(8); 477 } 478 479 /* 480 * An inconsistency occured which shouldn't during normal operations. 481 * Die if preening, otherwise just printf. 482 */ 483 /* VARARGS1 */ 484 pfatal(s, a1, a2, a3) 485 char *s; 486 { 487 488 if (preen) { 489 printf("%s: ", devname); 490 printf(s, a1, a2, a3); 491 printf("\n"); 492 printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", 493 devname); 494 exit(8); 495 } 496 printf(s, a1, a2, a3); 497 } 498 499 /* 500 * Pwarn is like printf when not preening, 501 * or a warning (preceded by filename) when preening. 502 */ 503 /* VARARGS1 */ 504 pwarn(s, a1, a2, a3, a4, a5, a6) 505 char *s; 506 { 507 508 if (preen) 509 printf("%s: ", devname); 510 printf(s, a1, a2, a3, a4, a5, a6); 511 } 512 513 #ifndef lint 514 /* 515 * Stub for routines from kernel. 516 */ 517 panic(s) 518 char *s; 519 { 520 521 pfatal("INTERNAL INCONSISTENCY:"); 522 errexit(s); 523 } 524 #endif 525