1 /* $NetBSD: utilities.c,v 1.8 2001/07/13 20:30:19 perseant Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/param.h> 37 #include <sys/time.h> 38 #include <ufs/ufs/dinode.h> 39 #include <ufs/ufs/dir.h> 40 #include <sys/mount.h> 41 #include <ufs/lfs/lfs.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <ctype.h> 46 #include <unistd.h> 47 48 #include <signal.h> 49 50 #include "fsutil.h" 51 #include "fsck.h" 52 #include "extern.h" 53 54 long diskreads, totalreads; /* Disk cache statistics */ 55 56 static void rwerror(char *, daddr_t); 57 58 extern int returntosingle; 59 60 int 61 ftypeok(struct dinode * dp) 62 { 63 switch (dp->di_mode & IFMT) { 64 65 case IFDIR: 66 case IFREG: 67 case IFBLK: 68 case IFCHR: 69 case IFLNK: 70 case IFSOCK: 71 case IFIFO: 72 return (1); 73 74 default: 75 if (debug) 76 printf("bad file type 0%o\n", dp->di_mode); 77 return (0); 78 } 79 } 80 81 int 82 reply(char *question) 83 { 84 int persevere; 85 char c; 86 87 if (preen) 88 pfatal("INTERNAL ERROR: GOT TO reply()"); 89 persevere = !strcmp(question, "CONTINUE"); 90 printf("\n"); 91 if (!persevere && (nflag || fswritefd < 0)) { 92 printf("%s? no\n\n", question); 93 return (0); 94 } 95 if (yflag || (persevere && nflag)) { 96 printf("%s? yes\n\n", question); 97 return (1); 98 } 99 do { 100 printf("%s? [yn] ", question); 101 (void)fflush(stdout); 102 c = getc(stdin); 103 while (c != '\n' && getc(stdin) != '\n') 104 if (feof(stdin)) 105 return (0); 106 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 107 printf("\n"); 108 if (c == 'y' || c == 'Y') 109 return (1); 110 return (0); 111 } 112 113 /* 114 * Malloc buffers and set up cache. 115 */ 116 void 117 bufinit() 118 { 119 register struct bufarea *bp; 120 long bufcnt, i; 121 char *bufp; 122 123 pbp = pdirbp = (struct bufarea *)0; 124 bufp = malloc((unsigned int)sblock.lfs_bsize); 125 if (bufp == 0) 126 errexit("cannot allocate buffer pool\n"); 127 /* cgblk.b_un.b_buf = bufp; */ 128 /* initbarea(&cgblk); */ 129 bufhead.b_next = bufhead.b_prev = &bufhead; 130 bufcnt = MAXBUFSPACE / sblock.lfs_bsize; 131 if (bufcnt < MINBUFS) 132 bufcnt = MINBUFS; 133 for (i = 0; i < bufcnt; i++) { 134 bp = (struct bufarea *)malloc(sizeof(struct bufarea)); 135 bufp = malloc((unsigned int)sblock.lfs_bsize); 136 if (bp == NULL || bufp == NULL) { 137 if (i >= MINBUFS) 138 break; 139 errexit("cannot allocate buffer pool\n"); 140 } 141 bp->b_un.b_buf = bufp; 142 bp->b_prev = &bufhead; 143 bp->b_next = bufhead.b_next; 144 bufhead.b_next->b_prev = bp; 145 bufhead.b_next = bp; 146 initbarea(bp); 147 } 148 bufhead.b_size = i; /* save number of buffers */ 149 } 150 151 /* 152 * Manage a cache of directory blocks. 153 */ 154 struct bufarea * 155 getddblk(daddr_t blkno, long size) 156 { 157 register struct bufarea *bp; 158 159 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 160 if (bp->b_bno == blkno) { 161 if (bp->b_size <= size) 162 getdblk(bp, blkno, size); 163 goto foundit; 164 } 165 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 166 if ((bp->b_flags & B_INUSE) == 0) 167 break; 168 if (bp == &bufhead) 169 errexit("deadlocked buffer pool\n"); 170 getdblk(bp, blkno, size); 171 /* fall through */ 172 foundit: 173 totalreads++; 174 bp->b_prev->b_next = bp->b_next; 175 bp->b_next->b_prev = bp->b_prev; 176 bp->b_prev = &bufhead; 177 bp->b_next = bufhead.b_next; 178 bufhead.b_next->b_prev = bp; 179 bufhead.b_next = bp; 180 bp->b_flags |= B_INUSE; 181 return (bp); 182 } 183 184 struct bufarea * 185 getdatablk(daddr_t blkno, long size) 186 { 187 return getddblk(blkno, size); 188 } 189 190 void 191 getdblk(struct bufarea * bp, daddr_t blk, long size) 192 { 193 if (bp->b_bno != blk) { 194 flush(fswritefd, bp); 195 diskreads++; 196 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, 197 fsbtodb(&sblock, blk), size); 198 bp->b_bno = blk; 199 bp->b_size = size; 200 } 201 } 202 203 204 void 205 getblk(struct bufarea * bp, daddr_t blk, long size) 206 { 207 getdblk(bp, blk, size); 208 } 209 210 void 211 flush(int fd, struct bufarea * bp) 212 { 213 if (!bp->b_dirty) 214 return; 215 if (bp->b_errs != 0) 216 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 217 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 218 bp->b_bno); 219 bp->b_dirty = 0; 220 bp->b_errs = 0; 221 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 222 if (bp != &sblk) 223 return; 224 #if 0 /* XXX - FFS */ 225 for (i = 0, j = 0; i < sblock.lfs_cssize; i += sblock.lfs_bsize, j++) { 226 bwrite(fswritefd, (char *)sblock.lfs_csp[j], 227 fsbtodb(&sblock, sblock.lfs_csaddr + j * sblock.lfs_frag), 228 sblock.lfs_cssize - i < sblock.lfs_bsize ? 229 sblock.lfs_cssize - i : sblock.lfs_bsize); 230 } 231 #endif 232 } 233 234 static void 235 rwerror(char *mesg, daddr_t blk) 236 { 237 238 if (preen == 0) 239 printf("\n"); 240 pfatal("CANNOT %s: BLK %d", mesg, blk); 241 if (reply("CONTINUE") == 0) 242 errexit("Program terminated\n"); 243 } 244 245 void 246 ckfini(int markclean) 247 { 248 register struct bufarea *bp, *nbp; 249 int cnt = 0; 250 251 if (fswritefd < 0) { 252 (void)close(fsreadfd); 253 return; 254 } 255 flush(fswritefd, &sblk); 256 if (havesb && sblk.b_bno != sblock.lfs_sboffs[0] && 257 sblk.b_bno != sblock.lfs_sboffs[1] && 258 !preen && reply("UPDATE STANDARD SUPERBLOCKS")) { 259 sblk.b_bno = fsbtodb(&sblock, sblock.lfs_sboffs[0]); 260 sbdirty(); 261 flush(fswritefd, &sblk); 262 } 263 if (havesb) { 264 if (sblk.b_bno == fsbtodb(&sblock, sblock.lfs_sboffs[0])) { 265 /* Do the first alternate */ 266 sblk.b_bno = fsbtodb(&sblock, sblock.lfs_sboffs[1]); 267 sbdirty(); 268 flush(fswritefd, &sblk); 269 } else if (sblk.b_bno == 270 fsbtodb(&sblock, sblock.lfs_sboffs[1])) { 271 /* Do the primary */ 272 sblk.b_bno = LFS_LABELPAD / dev_bsize; 273 sbdirty(); 274 flush(fswritefd, &sblk); 275 } 276 } 277 /* flush(fswritefd, &cgblk); */ 278 /* free(cgblk.b_un.b_buf); */ 279 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 280 cnt++; 281 flush(fswritefd, bp); 282 nbp = bp->b_prev; 283 free(bp->b_un.b_buf); 284 free((char *)bp); 285 } 286 if (bufhead.b_size != cnt) 287 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 288 pbp = pdirbp = (struct bufarea *)0; 289 if (markclean && !(sblock.lfs_pflags & LFS_PF_CLEAN)) { 290 /* 291 * Mark the file system as clean, and sync the superblock. 292 */ 293 if (preen) 294 pwarn("MARKING FILE SYSTEM CLEAN\n"); 295 else if (!reply("MARK FILE SYSTEM CLEAN")) 296 markclean = 0; 297 if (markclean) { 298 sblock.lfs_pflags |= LFS_PF_CLEAN; 299 sbdirty(); 300 flush(fswritefd, &sblk); 301 if (sblk.b_bno == LFS_LABELPAD / dev_bsize) { 302 /* Do the first alternate */ 303 sblk.b_bno = fsbtodb(&sblock, 304 sblock.lfs_sboffs[0]); 305 flush(fswritefd, &sblk); 306 } else if (sblk.b_bno == fsbtodb(&sblock, 307 sblock.lfs_sboffs[0])) { 308 /* Do the primary */ 309 sblk.b_bno = LFS_LABELPAD / dev_bsize; 310 flush(fswritefd, &sblk); 311 } 312 } 313 } 314 if (debug) 315 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 316 totalreads, (int)(diskreads * 100 / totalreads)); 317 (void)close(fsreadfd); 318 (void)close(fswritefd); 319 } 320 321 int 322 bread(int fd, char *buf, daddr_t blk, long size) 323 { 324 char *cp; 325 int i, errs; 326 off_t offset; 327 328 offset = blk; 329 offset *= dev_bsize; 330 if (lseek(fd, offset, 0) < 0) { 331 rwerror("SEEK", blk); 332 } else if (read(fd, buf, (int)size) == size) 333 return (0); 334 rwerror("READ", blk); 335 if (lseek(fd, offset, 0) < 0) 336 rwerror("SEEK", blk); 337 errs = 0; 338 memset(buf, 0, (size_t)size); 339 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 340 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 341 if (read(fd, cp, (int)secsize) != secsize) { 342 (void)lseek(fd, offset + i + secsize, 0); 343 if (secsize != dev_bsize && dev_bsize != 1) 344 printf(" %ld (%ld),", 345 (blk * dev_bsize + i) / secsize, 346 blk + i / dev_bsize); 347 else 348 printf(" %ld,", blk + i / dev_bsize); 349 errs++; 350 } 351 } 352 printf("\n"); 353 return (errs); 354 } 355 356 void 357 bwrite(int fd, char *buf, daddr_t blk, long size) 358 { 359 int i; 360 char *cp; 361 off_t offset; 362 363 if (fd < 0) 364 return; 365 offset = blk; 366 offset *= dev_bsize; 367 if (lseek(fd, offset, 0) < 0) 368 rwerror("SEEK", blk); 369 else if (write(fd, buf, (int)size) == size) { 370 fsmodified = 1; 371 return; 372 } 373 rwerror("WRITE", blk); 374 if (lseek(fd, offset, 0) < 0) 375 rwerror("SEEK", blk); 376 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 377 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 378 if (write(fd, cp, (int)dev_bsize) != dev_bsize) { 379 (void)lseek(fd, offset + i + dev_bsize, 0); 380 printf(" %ld,", blk + i / dev_bsize); 381 } 382 printf("\n"); 383 return; 384 } 385 386 /* 387 * allocate a data block with the specified number of fragments 388 */ 389 int 390 allocblk(long frags) 391 { 392 #if 1 393 /* 394 * XXX Can't allocate blocks right now because we would have to do 395 * a full partial segment write. 396 */ 397 return 0; 398 #else /* 0 */ 399 register int i, j, k; 400 401 if (frags <= 0 || frags > sblock.lfs_frag) 402 return (0); 403 for (i = 0; i < maxfsblock - sblock.lfs_frag; i += sblock.lfs_frag) { 404 for (j = 0; j <= sblock.lfs_frag - frags; j++) { 405 if (testbmap(i + j)) 406 continue; 407 for (k = 1; k < frags; k++) 408 if (testbmap(i + j + k)) 409 break; 410 if (k < frags) { 411 j += k; 412 continue; 413 } 414 for (k = 0; k < frags; k++) { 415 #ifndef VERBOSE_BLOCKMAP 416 setbmap(i + j + k); 417 #else 418 setbmap(i + j + k, -1); 419 #endif 420 } 421 n_blks += frags; 422 return (i + j); 423 } 424 } 425 return (0); 426 #endif /* 0 */ 427 } 428 429 /* 430 * Free a previously allocated block 431 */ 432 void 433 freeblk(daddr_t blkno, long frags) 434 { 435 struct inodesc idesc; 436 437 idesc.id_blkno = blkno; 438 idesc.id_numfrags = frags; 439 (void)pass4check(&idesc); 440 } 441 442 /* 443 * Find a pathname 444 */ 445 void 446 getpathname(char *namebuf, ino_t curdir, ino_t ino) 447 { 448 int len; 449 register char *cp; 450 struct inodesc idesc; 451 static int busy = 0; 452 453 if (curdir == ino && ino == ROOTINO) { 454 (void)strcpy(namebuf, "/"); 455 return; 456 } 457 if (busy || 458 (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { 459 (void)strcpy(namebuf, "?"); 460 return; 461 } 462 busy = 1; 463 memset(&idesc, 0, sizeof(struct inodesc)); 464 idesc.id_type = DATA; 465 idesc.id_fix = IGNORE; 466 cp = &namebuf[MAXPATHLEN - 1]; 467 *cp = '\0'; 468 if (curdir != ino) { 469 idesc.id_parent = curdir; 470 goto namelookup; 471 } 472 while (ino != ROOTINO) { 473 idesc.id_number = ino; 474 idesc.id_func = findino; 475 idesc.id_name = ".."; 476 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 477 break; 478 namelookup: 479 idesc.id_number = idesc.id_parent; 480 idesc.id_parent = ino; 481 idesc.id_func = findname; 482 idesc.id_name = namebuf; 483 if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0) 484 break; 485 len = strlen(namebuf); 486 cp -= len; 487 memcpy(cp, namebuf, (size_t)len); 488 *--cp = '/'; 489 if (cp < &namebuf[MAXNAMLEN]) 490 break; 491 ino = idesc.id_number; 492 } 493 busy = 0; 494 if (ino != ROOTINO) 495 *--cp = '?'; 496 memcpy(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 497 } 498 499 void 500 catch(int n) 501 { 502 if (!doinglevel2) 503 ckfini(0); 504 exit(12); 505 } 506 507 /* 508 * When preening, allow a single quit to signal 509 * a special exit after filesystem checks complete 510 * so that reboot sequence may be interrupted. 511 */ 512 void 513 catchquit(int n) 514 { 515 printf("returning to single-user after filesystem check\n"); 516 returntosingle = 1; 517 (void)signal(SIGQUIT, SIG_DFL); 518 } 519 520 /* 521 * Ignore a single quit signal; wait and flush just in case. 522 * Used by child processes in preen. 523 */ 524 void 525 voidquit(int n) 526 { 527 528 sleep(1); 529 (void)signal(SIGQUIT, SIG_IGN); 530 (void)signal(SIGQUIT, SIG_DFL); 531 } 532 533 /* 534 * determine whether an inode should be fixed. 535 */ 536 int 537 dofix(struct inodesc * idesc, char *msg) 538 { 539 540 switch (idesc->id_fix) { 541 542 case DONTKNOW: 543 if (idesc->id_type == DATA) 544 direrror(idesc->id_number, msg); 545 else 546 pwarn("%s", msg); 547 if (preen) { 548 printf(" (SALVAGED)\n"); 549 idesc->id_fix = FIX; 550 return (ALTERED); 551 } 552 if (reply("SALVAGE") == 0) { 553 idesc->id_fix = NOFIX; 554 return (0); 555 } 556 idesc->id_fix = FIX; 557 return (ALTERED); 558 559 case FIX: 560 return (ALTERED); 561 562 case NOFIX: 563 case IGNORE: 564 return (0); 565 566 default: 567 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 568 } 569 /* NOTREACHED */ 570 } 571