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