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