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