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.19 (Berkeley) 02/01/90"; 9 #endif not lint 10 11 #include <sys/param.h> 12 #include <ufs/dinode.h> 13 #include <ufs/fs.h> 14 #include <ufs/dir.h> 15 #include <stdio.h> 16 #include <ctype.h> 17 #include "fsck.h" 18 19 long diskreads, totalreads; /* Disk cache statistics */ 20 long lseek(); 21 char *malloc(); 22 23 ftypeok(dp) 24 struct dinode *dp; 25 { 26 switch (dp->di_mode & IFMT) { 27 28 case IFDIR: 29 case IFREG: 30 case IFBLK: 31 case IFCHR: 32 case IFLNK: 33 case IFSOCK: 34 return (1); 35 36 default: 37 if (debug) 38 printf("bad file type 0%o\n", dp->di_mode); 39 return (0); 40 } 41 } 42 43 reply(mesg) 44 char *mesg; 45 { 46 char line[80]; 47 int cont = (strcmp(mesg, "CONTINUE") == 0); 48 49 if (preen) 50 pfatal("INTERNAL ERROR: GOT TO reply()"); 51 printf("\n%s? ", mesg); 52 if (!cont && (nflag || fswritefd < 0)) { 53 printf(" no\n\n"); 54 return (0); 55 } 56 if (yflag || (cont && nflag)) { 57 printf(" yes\n\n"); 58 return (1); 59 } 60 if (getline(stdin, line, sizeof(line)) == EOF) 61 errexit("\n"); 62 printf("\n"); 63 if (line[0] == 'y' || line[0] == 'Y') 64 return (1); 65 else 66 return (0); 67 } 68 69 getline(fp, loc, maxlen) 70 FILE *fp; 71 char *loc; 72 int maxlen; 73 { 74 register long 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 struct bufarea *bp; 95 long bufcnt, i; 96 char *bufp; 97 98 bufp = malloc((unsigned int)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 = (struct bufarea *)malloc(sizeof(struct bufarea)); 109 bufp = malloc((unsigned int)sblock.fs_bsize); 110 if (bp == NULL || bufp == NULL) { 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 struct bufarea * 129 getdatablk(blkno, size) 130 daddr_t blkno; 131 long size; 132 { 133 register struct 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 struct bufarea * 158 getblk(bp, blk, size) 159 register struct bufarea *bp; 160 daddr_t blk; 161 long size; 162 { 163 daddr_t dblk; 164 165 dblk = fsbtodb(&sblock, blk); 166 if (bp->b_bno == dblk) 167 return (bp); 168 flush(fswritefd, bp); 169 diskreads++; 170 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 171 bp->b_bno = dblk; 172 bp->b_size = size; 173 return (bp); 174 } 175 176 flush(fd, bp) 177 int fd; 178 register struct 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(fd, 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(fswritefd, (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 rwerror(mesg, blk) 202 char *mesg; 203 daddr_t blk; 204 { 205 206 if (preen == 0) 207 printf("\n"); 208 pfatal("CANNOT %s: BLK %ld", mesg, blk); 209 if (reply("CONTINUE") == 0) 210 errexit("Program terminated\n"); 211 } 212 213 ckfini() 214 { 215 register struct bufarea *bp, *nbp; 216 int cnt = 0; 217 218 flush(fswritefd, &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(fswritefd, &sblk); 224 } 225 flush(fswritefd, &cgblk); 226 free(cgblk.b_un.b_buf); 227 for (bp = bufhead.b_prev; bp != &bufhead; bp = nbp) { 228 cnt++; 229 flush(fswritefd, bp); 230 nbp = bp->b_prev; 231 free(bp->b_un.b_buf); 232 free((char *)bp); 233 } 234 if (bufhead.b_size != cnt) 235 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 236 if (debug) 237 printf("cache missed %d of %d (%d%%)\n", diskreads, 238 totalreads, diskreads * 100 / totalreads); 239 (void)close(fsreadfd); 240 (void)close(fswritefd); 241 } 242 243 bread(fd, buf, blk, size) 244 int fd; 245 char *buf; 246 daddr_t blk; 247 long size; 248 { 249 char *cp; 250 int i, errs; 251 252 if (lseek(fd, blk * dev_bsize, 0) < 0) 253 rwerror("SEEK", blk); 254 else if (read(fd, buf, (int)size) == size) 255 return (0); 256 rwerror("READ", blk); 257 if (lseek(fd, blk * dev_bsize, 0) < 0) 258 rwerror("SEEK", blk); 259 errs = 0; 260 bzero(buf, (int)size); 261 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 262 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 263 if (read(fd, cp, (int)secsize) < 0) { 264 lseek(fd, blk * dev_bsize + i + secsize, 0); 265 if (secsize != dev_bsize && dev_bsize != 1) 266 printf(" %d (%d),", 267 (blk * dev_bsize + i) / secsize, 268 blk + i / dev_bsize); 269 else 270 printf(" %d,", blk + i / dev_bsize); 271 errs++; 272 } 273 } 274 printf("\n"); 275 return (errs); 276 } 277 278 bwrite(fd, buf, blk, size) 279 int fd; 280 char *buf; 281 daddr_t blk; 282 long size; 283 { 284 int i; 285 char *cp; 286 287 if (fd < 0) 288 return; 289 if (lseek(fd, blk * dev_bsize, 0) < 0) 290 rwerror("SEEK", blk); 291 else if (write(fd, buf, (int)size) == size) { 292 fsmodified = 1; 293 return; 294 } 295 rwerror("WRITE", blk); 296 if (lseek(fd, blk * dev_bsize, 0) < 0) 297 rwerror("SEEK", blk); 298 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 299 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 300 if (write(fd, cp, (int)dev_bsize) < 0) { 301 lseek(fd, blk * dev_bsize + i + dev_bsize, 0); 302 printf(" %d,", blk + i / dev_bsize); 303 } 304 printf("\n"); 305 return; 306 } 307 308 /* 309 * allocate a data block with the specified number of fragments 310 */ 311 allocblk(frags) 312 long frags; 313 { 314 register int i, j, k; 315 316 if (frags <= 0 || frags > sblock.fs_frag) 317 return (0); 318 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 319 for (j = 0; j <= sblock.fs_frag - frags; j++) { 320 if (testbmap(i + j)) 321 continue; 322 for (k = 1; k < frags; k++) 323 if (testbmap(i + j + k)) 324 break; 325 if (k < frags) { 326 j += k; 327 continue; 328 } 329 for (k = 0; k < frags; k++) 330 setbmap(i + j + k); 331 n_blks += frags; 332 return (i + j); 333 } 334 } 335 return (0); 336 } 337 338 /* 339 * Free a previously allocated block 340 */ 341 freeblk(blkno, frags) 342 daddr_t blkno; 343 long frags; 344 { 345 struct inodesc idesc; 346 347 idesc.id_blkno = blkno; 348 idesc.id_numfrags = frags; 349 pass4check(&idesc); 350 } 351 352 /* 353 * Find a pathname 354 */ 355 getpathname(namebuf, curdir, ino) 356 char *namebuf; 357 ino_t curdir, ino; 358 { 359 int len; 360 register char *cp; 361 struct inodesc idesc; 362 extern int findname(); 363 364 if (statemap[ino] != DSTATE && statemap[ino] != DFOUND) { 365 strcpy(namebuf, "?"); 366 return; 367 } 368 bzero((char *)&idesc, sizeof(struct inodesc)); 369 idesc.id_type = DATA; 370 cp = &namebuf[BUFSIZ - 1]; 371 *cp = '\0'; 372 if (curdir != ino) { 373 idesc.id_parent = curdir; 374 goto namelookup; 375 } 376 while (ino != ROOTINO) { 377 idesc.id_number = ino; 378 idesc.id_func = findino; 379 idesc.id_name = ".."; 380 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 381 break; 382 namelookup: 383 idesc.id_number = idesc.id_parent; 384 idesc.id_parent = ino; 385 idesc.id_func = findname; 386 idesc.id_name = namebuf; 387 if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0) 388 break; 389 len = strlen(namebuf); 390 cp -= len; 391 if (cp < &namebuf[MAXNAMLEN]) 392 break; 393 bcopy(namebuf, cp, len); 394 *--cp = '/'; 395 ino = idesc.id_number; 396 } 397 if (ino != ROOTINO) { 398 strcpy(namebuf, "?"); 399 return; 400 } 401 bcopy(cp, namebuf, &namebuf[BUFSIZ] - cp); 402 } 403 404 void 405 catch() 406 { 407 ckfini(); 408 exit(12); 409 } 410 411 /* 412 * When preening, allow a single quit to signal 413 * a special exit after filesystem checks complete 414 * so that reboot sequence may be interrupted. 415 */ 416 void 417 catchquit() 418 { 419 extern returntosingle; 420 421 printf("returning to single-user after filesystem check\n"); 422 returntosingle = 1; 423 (void)signal(SIGQUIT, SIG_DFL); 424 } 425 426 /* 427 * Ignore a single quit signal; wait and flush just in case. 428 * Used by child processes in preen. 429 */ 430 void 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 direrror(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 unexpected inconsistency occured. 488 * Die if preening, otherwise just print message and continue. 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 just prints a message 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