1 /* 2 * Copyright (c) 1980, 1986 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)utilities.c 5.26 (Berkeley) 06/01/90"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <ufs/dinode.h> 14 #include <ufs/fs.h> 15 #include <ufs/dir.h> 16 #include <stdio.h> 17 #include <ctype.h> 18 #include "fsck.h" 19 20 long diskreads, totalreads; /* Disk cache statistics */ 21 long lseek(); 22 char *malloc(); 23 24 ftypeok(dp) 25 struct 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(question) 45 char *question; 46 { 47 int persevere; 48 char c; 49 50 if (preen) 51 pfatal("INTERNAL ERROR: GOT TO reply()"); 52 persevere = !strcmp(question, "CONTINUE"); 53 printf("\n"); 54 if (!persevere && (nflag || fswritefd < 0)) { 55 printf("%s? no\n\n", question); 56 return (0); 57 } 58 if (yflag || (persevere && nflag)) { 59 printf("%s? yes\n\n", question); 60 return (1); 61 } 62 do { 63 printf("%s? [yn] ", question); 64 (void) fflush(stdout); 65 c = getc(stdin); 66 while (c != '\n' && getc(stdin) != '\n') 67 if (feof(stdin)) 68 return (0); 69 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 70 printf("\n"); 71 if (c == 'y' || c == 'Y') 72 return (1); 73 return (0); 74 } 75 76 /* 77 * Malloc buffers and set up cache. 78 */ 79 bufinit() 80 { 81 register struct bufarea *bp; 82 long bufcnt, i; 83 char *bufp; 84 85 pbp = pdirbp = (struct bufarea *)0; 86 bufp = malloc((unsigned int)sblock.fs_bsize); 87 if (bufp == 0) 88 errexit("cannot allocate buffer pool\n"); 89 cgblk.b_un.b_buf = bufp; 90 initbarea(&cgblk); 91 bufhead.b_next = bufhead.b_prev = &bufhead; 92 bufcnt = MAXBUFSPACE / sblock.fs_bsize; 93 if (bufcnt < MINBUFS) 94 bufcnt = MINBUFS; 95 for (i = 0; i < bufcnt; i++) { 96 bp = (struct bufarea *)malloc(sizeof(struct bufarea)); 97 bufp = malloc((unsigned int)sblock.fs_bsize); 98 if (bp == NULL || bufp == NULL) { 99 if (i >= MINBUFS) 100 break; 101 errexit("cannot allocate buffer pool\n"); 102 } 103 bp->b_un.b_buf = bufp; 104 bp->b_prev = &bufhead; 105 bp->b_next = bufhead.b_next; 106 bufhead.b_next->b_prev = bp; 107 bufhead.b_next = bp; 108 initbarea(bp); 109 } 110 bufhead.b_size = i; /* save number of buffers */ 111 } 112 113 /* 114 * Manage a cache of directory blocks. 115 */ 116 struct bufarea * 117 getdatablk(blkno, size) 118 daddr_t blkno; 119 long size; 120 { 121 register struct bufarea *bp; 122 123 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 124 if (bp->b_bno == fsbtodb(&sblock, blkno)) 125 goto foundit; 126 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 127 if ((bp->b_flags & B_INUSE) == 0) 128 break; 129 if (bp == &bufhead) 130 errexit("deadlocked buffer pool\n"); 131 getblk(bp, blkno, size); 132 /* fall through */ 133 foundit: 134 totalreads++; 135 bp->b_prev->b_next = bp->b_next; 136 bp->b_next->b_prev = bp->b_prev; 137 bp->b_prev = &bufhead; 138 bp->b_next = bufhead.b_next; 139 bufhead.b_next->b_prev = bp; 140 bufhead.b_next = bp; 141 bp->b_flags |= B_INUSE; 142 return (bp); 143 } 144 145 struct bufarea * 146 getblk(bp, blk, size) 147 register struct bufarea *bp; 148 daddr_t blk; 149 long size; 150 { 151 daddr_t dblk; 152 153 dblk = fsbtodb(&sblock, blk); 154 if (bp->b_bno == dblk) 155 return (bp); 156 flush(fswritefd, bp); 157 diskreads++; 158 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 159 bp->b_bno = dblk; 160 bp->b_size = size; 161 return (bp); 162 } 163 164 flush(fd, bp) 165 int fd; 166 register struct bufarea *bp; 167 { 168 register int i, j; 169 170 if (!bp->b_dirty) 171 return; 172 if (bp->b_errs != 0) 173 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 174 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 175 bp->b_bno); 176 bp->b_dirty = 0; 177 bp->b_errs = 0; 178 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 179 if (bp != &sblk) 180 return; 181 for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 182 bwrite(fswritefd, (char *)sblock.fs_csp[j], 183 fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 184 sblock.fs_cssize - i < sblock.fs_bsize ? 185 sblock.fs_cssize - i : sblock.fs_bsize); 186 } 187 } 188 189 rwerror(mesg, blk) 190 char *mesg; 191 daddr_t blk; 192 { 193 194 if (preen == 0) 195 printf("\n"); 196 pfatal("CANNOT %s: BLK %ld", mesg, blk); 197 if (reply("CONTINUE") == 0) 198 errexit("Program terminated\n"); 199 } 200 201 ckfini() 202 { 203 register struct bufarea *bp, *nbp; 204 int cnt = 0; 205 206 flush(fswritefd, &sblk); 207 if (havesb && sblk.b_bno != SBOFF / dev_bsize && 208 !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 209 sblk.b_bno = SBOFF / dev_bsize; 210 sbdirty(); 211 flush(fswritefd, &sblk); 212 } 213 flush(fswritefd, &cgblk); 214 free(cgblk.b_un.b_buf); 215 for (bp = bufhead.b_prev; bp != &bufhead; bp = nbp) { 216 cnt++; 217 flush(fswritefd, bp); 218 nbp = bp->b_prev; 219 free(bp->b_un.b_buf); 220 free((char *)bp); 221 } 222 if (bufhead.b_size != cnt) 223 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 224 pbp = pdirbp = (struct bufarea *)0; 225 if (debug) 226 printf("cache missed %d of %d (%d%%)\n", diskreads, 227 totalreads, diskreads * 100 / totalreads); 228 (void)close(fsreadfd); 229 (void)close(fswritefd); 230 } 231 232 bread(fd, buf, blk, size) 233 int fd; 234 char *buf; 235 daddr_t blk; 236 long size; 237 { 238 char *cp; 239 int i, errs; 240 241 if (lseek(fd, blk * dev_bsize, 0) < 0) 242 rwerror("SEEK", blk); 243 else if (read(fd, buf, (int)size) == size) 244 return (0); 245 rwerror("READ", blk); 246 if (lseek(fd, blk * dev_bsize, 0) < 0) 247 rwerror("SEEK", blk); 248 errs = 0; 249 bzero(buf, (int)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(fd, cp, (int)secsize) < 0) { 253 lseek(fd, 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(fd, buf, blk, size) 268 int fd; 269 char *buf; 270 daddr_t blk; 271 long size; 272 { 273 int i; 274 char *cp; 275 276 if (fd < 0) 277 return; 278 if (lseek(fd, blk * dev_bsize, 0) < 0) 279 rwerror("SEEK", blk); 280 else if (write(fd, buf, (int)size) == size) { 281 fsmodified = 1; 282 return; 283 } 284 rwerror("WRITE", blk); 285 if (lseek(fd, blk * dev_bsize, 0) < 0) 286 rwerror("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(fd, cp, (int)dev_bsize) < 0) { 290 lseek(fd, 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 long 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 < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 308 for (j = 0; j <= sblock.fs_frag - frags; j++) { 309 if (testbmap(i + j)) 310 continue; 311 for (k = 1; k < frags; k++) 312 if (testbmap(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 long 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[curdir] != DSTATE && statemap[curdir] != DFOUND) { 354 strcpy(namebuf, "?"); 355 return; 356 } 357 bzero((char *)&idesc, sizeof(struct inodesc)); 358 idesc.id_type = DATA; 359 cp = &namebuf[MAXPATHLEN - 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[MAXPATHLEN] - cp); 391 } 392 393 void 394 catch() 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 void 406 catchquit() 407 { 408 extern returntosingle; 409 410 printf("returning to single-user after filesystem check\n"); 411 returntosingle = 1; 412 (void)signal(SIGQUIT, SIG_DFL); 413 } 414 415 /* 416 * Ignore a single quit signal; wait and flush just in case. 417 * Used by child processes in preen. 418 */ 419 void 420 voidquit() 421 { 422 423 sleep(1); 424 (void)signal(SIGQUIT, SIG_IGN); 425 (void)signal(SIGQUIT, SIG_DFL); 426 } 427 428 /* 429 * determine whether an inode should be fixed. 430 */ 431 dofix(idesc, msg) 432 register struct inodesc *idesc; 433 char *msg; 434 { 435 436 switch (idesc->id_fix) { 437 438 case DONTKNOW: 439 if (idesc->id_type == DATA) 440 direrror(idesc->id_number, msg); 441 else 442 pwarn(msg); 443 if (preen) { 444 printf(" (SALVAGED)\n"); 445 idesc->id_fix = FIX; 446 return (ALTERED); 447 } 448 if (reply("SALVAGE") == 0) { 449 idesc->id_fix = NOFIX; 450 return (0); 451 } 452 idesc->id_fix = FIX; 453 return (ALTERED); 454 455 case FIX: 456 return (ALTERED); 457 458 case NOFIX: 459 return (0); 460 461 default: 462 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 463 } 464 /* NOTREACHED */ 465 } 466 467 /* VARARGS1 */ 468 errexit(s1, s2, s3, s4) 469 char *s1; 470 { 471 printf(s1, s2, s3, s4); 472 exit(8); 473 } 474 475 /* 476 * An unexpected inconsistency occured. 477 * Die if preening, otherwise just print message and continue. 478 */ 479 /* VARARGS1 */ 480 pfatal(s, a1, a2, a3) 481 char *s; 482 { 483 484 if (preen) { 485 printf("%s: ", devname); 486 printf(s, a1, a2, a3); 487 printf("\n"); 488 printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", 489 devname); 490 exit(8); 491 } 492 printf(s, a1, a2, a3); 493 } 494 495 /* 496 * Pwarn just prints a message when not preening, 497 * or a warning (preceded by filename) when preening. 498 */ 499 /* VARARGS1 */ 500 pwarn(s, a1, a2, a3, a4, a5, a6) 501 char *s; 502 { 503 504 if (preen) 505 printf("%s: ", devname); 506 printf(s, a1, a2, a3, a4, a5, a6); 507 } 508 509 #ifndef lint 510 /* 511 * Stub for routines from kernel. 512 */ 513 panic(s) 514 char *s; 515 { 516 517 pfatal("INTERNAL INCONSISTENCY:"); 518 errexit(s); 519 } 520 #endif 521