1 /* $NetBSD: utilities.c,v 1.6 2000/10/10 20:24:51 is 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 int 59 ftypeok(struct dinode * dp) 60 { 61 switch (dp->di_mode & IFMT) { 62 63 case IFDIR: 64 case IFREG: 65 case IFBLK: 66 case IFCHR: 67 case IFLNK: 68 case IFSOCK: 69 case IFIFO: 70 return (1); 71 72 default: 73 if (debug) 74 printf("bad file type 0%o\n", dp->di_mode); 75 return (0); 76 } 77 } 78 79 int 80 reply(char *question) 81 { 82 int persevere; 83 char c; 84 85 if (preen) 86 pfatal("INTERNAL ERROR: GOT TO reply()"); 87 persevere = !strcmp(question, "CONTINUE"); 88 printf("\n"); 89 if (!persevere && (nflag || fswritefd < 0)) { 90 printf("%s? no\n\n", question); 91 return (0); 92 } 93 if (yflag || (persevere && nflag)) { 94 printf("%s? yes\n\n", question); 95 return (1); 96 } 97 do { 98 printf("%s? [yn] ", question); 99 (void)fflush(stdout); 100 c = getc(stdin); 101 while (c != '\n' && getc(stdin) != '\n') 102 if (feof(stdin)) 103 return (0); 104 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 105 printf("\n"); 106 if (c == 'y' || c == 'Y') 107 return (1); 108 return (0); 109 } 110 111 /* 112 * Malloc buffers and set up cache. 113 */ 114 void 115 bufinit() 116 { 117 register struct bufarea *bp; 118 long bufcnt, i; 119 char *bufp; 120 121 pbp = pdirbp = (struct bufarea *)0; 122 bufp = malloc((unsigned int)sblock.lfs_bsize); 123 if (bufp == 0) 124 errexit("cannot allocate buffer pool\n"); 125 /* cgblk.b_un.b_buf = bufp; */ 126 /* initbarea(&cgblk); */ 127 bufhead.b_next = bufhead.b_prev = &bufhead; 128 bufcnt = MAXBUFSPACE / sblock.lfs_bsize; 129 if (bufcnt < MINBUFS) 130 bufcnt = MINBUFS; 131 for (i = 0; i < bufcnt; i++) { 132 bp = (struct bufarea *)malloc(sizeof(struct bufarea)); 133 bufp = malloc((unsigned int)sblock.lfs_bsize); 134 if (bp == NULL || bufp == NULL) { 135 if (i >= MINBUFS) 136 break; 137 errexit("cannot allocate buffer pool\n"); 138 } 139 bp->b_un.b_buf = bufp; 140 bp->b_prev = &bufhead; 141 bp->b_next = bufhead.b_next; 142 bufhead.b_next->b_prev = bp; 143 bufhead.b_next = bp; 144 initbarea(bp); 145 } 146 bufhead.b_size = i; /* save number of buffers */ 147 } 148 149 /* 150 * Manage a cache of directory blocks. 151 */ 152 struct bufarea * 153 getddblk(daddr_t blkno, long size) 154 { 155 register struct bufarea *bp; 156 157 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 158 if (bp->b_bno == blkno) { 159 if (bp->b_size <= size) 160 getdblk(bp, blkno, size); 161 goto foundit; 162 } 163 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 164 if ((bp->b_flags & B_INUSE) == 0) 165 break; 166 if (bp == &bufhead) 167 errexit("deadlocked buffer pool\n"); 168 getdblk(bp, blkno, size); 169 /* fall through */ 170 foundit: 171 totalreads++; 172 bp->b_prev->b_next = bp->b_next; 173 bp->b_next->b_prev = bp->b_prev; 174 bp->b_prev = &bufhead; 175 bp->b_next = bufhead.b_next; 176 bufhead.b_next->b_prev = bp; 177 bufhead.b_next = bp; 178 bp->b_flags |= B_INUSE; 179 return (bp); 180 } 181 182 struct bufarea * 183 getdatablk(daddr_t blkno, long size) 184 { 185 return getddblk(fsbtodb(&sblock, blkno), size); 186 } 187 188 void 189 getdblk(struct bufarea * bp, daddr_t blk, long size) 190 { 191 if (bp->b_bno != blk) { 192 flush(fswritefd, bp); 193 diskreads++; 194 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, blk, size); 195 bp->b_bno = blk; 196 bp->b_size = size; 197 } 198 } 199 200 void 201 getblk(struct bufarea * bp, daddr_t blk, long size) 202 { 203 getdblk(bp, fsbtodb(&sblock, blk), size); 204 } 205 206 void 207 flush(int fd, struct bufarea * bp) 208 { 209 if (!bp->b_dirty) 210 return; 211 if (bp->b_errs != 0) 212 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", 213 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", 214 bp->b_bno); 215 bp->b_dirty = 0; 216 bp->b_errs = 0; 217 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 218 if (bp != &sblk) 219 return; 220 #if 0 /* XXX - FFS */ 221 for (i = 0, j = 0; i < sblock.lfs_cssize; i += sblock.lfs_bsize, j++) { 222 bwrite(fswritefd, (char *)sblock.lfs_csp[j], 223 fsbtodb(&sblock, sblock.lfs_csaddr + j * sblock.lfs_frag), 224 sblock.lfs_cssize - i < sblock.lfs_bsize ? 225 sblock.lfs_cssize - i : sblock.lfs_bsize); 226 } 227 #endif 228 } 229 230 static void 231 rwerror(char *mesg, daddr_t blk) 232 { 233 234 if (preen == 0) 235 printf("\n"); 236 pfatal("CANNOT %s: BLK %d", mesg, blk); 237 if (reply("CONTINUE") == 0) 238 errexit("Program terminated\n"); 239 } 240 241 void 242 ckfini(int markclean) 243 { 244 register struct bufarea *bp, *nbp; 245 int cnt = 0; 246 247 if (fswritefd < 0) { 248 (void)close(fsreadfd); 249 return; 250 } 251 flush(fswritefd, &sblk); 252 if (havesb && sblk.b_bno != LFS_LABELPAD / dev_bsize && 253 sblk.b_bno != sblock.lfs_sboffs[0] && 254 !preen && reply("UPDATE STANDARD SUPERBLOCKS")) { 255 sblk.b_bno = LFS_LABELPAD / dev_bsize; 256 sbdirty(); 257 flush(fswritefd, &sblk); 258 } 259 if (havesb) { 260 if (sblk.b_bno == LFS_LABELPAD / dev_bsize) { 261 /* Do the first alternate */ 262 sblk.b_bno = sblock.lfs_sboffs[1]; 263 sbdirty(); 264 flush(fswritefd, &sblk); 265 } else if (sblk.b_bno == sblock.lfs_sboffs[1]) { 266 /* Do the primary */ 267 sblk.b_bno = LFS_LABELPAD / dev_bsize; 268 sbdirty(); 269 flush(fswritefd, &sblk); 270 } 271 } 272 /* flush(fswritefd, &cgblk); */ 273 /* free(cgblk.b_un.b_buf); */ 274 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 275 cnt++; 276 flush(fswritefd, bp); 277 nbp = bp->b_prev; 278 free(bp->b_un.b_buf); 279 free((char *)bp); 280 } 281 if (bufhead.b_size != cnt) 282 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 283 pbp = pdirbp = (struct bufarea *)0; 284 if (markclean && sblock.lfs_clean == 0) { 285 /* 286 * Mark the file system as clean, and sync the superblock. 287 */ 288 if (preen) 289 pwarn("MARKING FILE SYSTEM CLEAN\n"); 290 else if (!reply("MARK FILE SYSTEM CLEAN")) 291 markclean = 0; 292 if (markclean) { 293 sblock.lfs_clean = 1; 294 sbdirty(); 295 flush(fswritefd, &sblk); 296 if (sblk.b_bno == LFS_LABELPAD / dev_bsize) { 297 /* Do the first alternate */ 298 sblk.b_bno = sblock.lfs_sboffs[0]; 299 flush(fswritefd, &sblk); 300 } else if (sblk.b_bno == sblock.lfs_sboffs[0]) { 301 /* Do the primary */ 302 sblk.b_bno = LFS_LABELPAD / dev_bsize; 303 flush(fswritefd, &sblk); 304 } 305 } 306 } 307 if (debug) 308 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 309 totalreads, (int)(diskreads * 100 / totalreads)); 310 (void)close(fsreadfd); 311 (void)close(fswritefd); 312 } 313 314 int 315 bread(int fd, char *buf, daddr_t blk, long size) 316 { 317 char *cp; 318 int i, errs; 319 off_t offset; 320 321 offset = blk; 322 offset *= dev_bsize; 323 if (lseek(fd, offset, 0) < 0) { 324 rwerror("SEEK", blk); 325 } else if (read(fd, buf, (int)size) == size) 326 return (0); 327 rwerror("READ", blk); 328 if (lseek(fd, offset, 0) < 0) 329 rwerror("SEEK", blk); 330 errs = 0; 331 memset(buf, 0, (size_t)size); 332 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 333 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 334 if (read(fd, cp, (int)secsize) != secsize) { 335 (void)lseek(fd, offset + i + secsize, 0); 336 if (secsize != dev_bsize && dev_bsize != 1) 337 printf(" %ld (%ld),", 338 (blk * dev_bsize + i) / secsize, 339 blk + i / dev_bsize); 340 else 341 printf(" %ld,", blk + i / dev_bsize); 342 errs++; 343 } 344 } 345 printf("\n"); 346 return (errs); 347 } 348 349 void 350 bwrite(int fd, char *buf, daddr_t blk, long size) 351 { 352 int i; 353 char *cp; 354 off_t offset; 355 356 if (fd < 0) 357 return; 358 offset = blk; 359 offset *= dev_bsize; 360 if (lseek(fd, offset, 0) < 0) 361 rwerror("SEEK", blk); 362 else if (write(fd, buf, (int)size) == size) { 363 fsmodified = 1; 364 return; 365 } 366 rwerror("WRITE", blk); 367 if (lseek(fd, offset, 0) < 0) 368 rwerror("SEEK", blk); 369 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 370 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) 371 if (write(fd, cp, (int)dev_bsize) != dev_bsize) { 372 (void)lseek(fd, offset + i + dev_bsize, 0); 373 printf(" %ld,", blk + i / dev_bsize); 374 } 375 printf("\n"); 376 return; 377 } 378 379 /* 380 * allocate a data block with the specified number of fragments 381 */ 382 int 383 allocblk(long frags) 384 { 385 #if 1 386 /* 387 * XXX Can't allocate blocks right now because we would have to do 388 * a full partial segment write. 389 */ 390 return 0; 391 #else /* 0 */ 392 register int i, j, k; 393 394 if (frags <= 0 || frags > sblock.lfs_frag) 395 return (0); 396 for (i = 0; i < maxfsblock - sblock.lfs_frag; i += sblock.lfs_frag) { 397 for (j = 0; j <= sblock.lfs_frag - frags; j++) { 398 if (testbmap(i + j)) 399 continue; 400 for (k = 1; k < frags; k++) 401 if (testbmap(i + j + k)) 402 break; 403 if (k < frags) { 404 j += k; 405 continue; 406 } 407 for (k = 0; k < frags; k++) { 408 #ifndef VERBOSE_BLOCKMAP 409 setbmap(i + j + k); 410 #else 411 setbmap(i + j + k, -1); 412 #endif 413 } 414 n_blks += frags; 415 return (i + j); 416 } 417 } 418 return (0); 419 #endif /* 0 */ 420 } 421 422 /* 423 * Free a previously allocated block 424 */ 425 void 426 freeblk(daddr_t blkno, long frags) 427 { 428 struct inodesc idesc; 429 430 idesc.id_blkno = blkno; 431 idesc.id_numfrags = frags; 432 (void)pass4check(&idesc); 433 } 434 435 /* 436 * Find a pathname 437 */ 438 void 439 getpathname(char *namebuf, ino_t curdir, ino_t ino) 440 { 441 int len; 442 register char *cp; 443 struct inodesc idesc; 444 static int busy = 0; 445 446 if (curdir == ino && ino == ROOTINO) { 447 (void)strcpy(namebuf, "/"); 448 return; 449 } 450 if (busy || 451 (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { 452 (void)strcpy(namebuf, "?"); 453 return; 454 } 455 busy = 1; 456 memset(&idesc, 0, sizeof(struct inodesc)); 457 idesc.id_type = DATA; 458 idesc.id_fix = IGNORE; 459 cp = &namebuf[MAXPATHLEN - 1]; 460 *cp = '\0'; 461 if (curdir != ino) { 462 idesc.id_parent = curdir; 463 goto namelookup; 464 } 465 while (ino != ROOTINO) { 466 idesc.id_number = ino; 467 idesc.id_func = findino; 468 idesc.id_name = ".."; 469 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 470 break; 471 namelookup: 472 idesc.id_number = idesc.id_parent; 473 idesc.id_parent = ino; 474 idesc.id_func = findname; 475 idesc.id_name = namebuf; 476 if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0) 477 break; 478 len = strlen(namebuf); 479 cp -= len; 480 memcpy(cp, namebuf, (size_t)len); 481 *--cp = '/'; 482 if (cp < &namebuf[MAXNAMLEN]) 483 break; 484 ino = idesc.id_number; 485 } 486 busy = 0; 487 if (ino != ROOTINO) 488 *--cp = '?'; 489 memcpy(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp)); 490 } 491 492 void 493 catch(int n) 494 { 495 if (!doinglevel2) 496 ckfini(0); 497 exit(12); 498 } 499 500 /* 501 * When preening, allow a single quit to signal 502 * a special exit after filesystem checks complete 503 * so that reboot sequence may be interrupted. 504 */ 505 void 506 catchquit(int n) 507 { 508 extern int returntosingle; 509 510 printf("returning to single-user after filesystem check\n"); 511 returntosingle = 1; 512 (void)signal(SIGQUIT, SIG_DFL); 513 } 514 515 /* 516 * Ignore a single quit signal; wait and flush just in case. 517 * Used by child processes in preen. 518 */ 519 void 520 voidquit(int n) 521 { 522 523 sleep(1); 524 (void)signal(SIGQUIT, SIG_IGN); 525 (void)signal(SIGQUIT, SIG_DFL); 526 } 527 528 /* 529 * determine whether an inode should be fixed. 530 */ 531 int 532 dofix(struct inodesc * idesc, char *msg) 533 { 534 535 switch (idesc->id_fix) { 536 537 case DONTKNOW: 538 if (idesc->id_type == DATA) 539 direrror(idesc->id_number, msg); 540 else 541 pwarn("%s", msg); 542 if (preen) { 543 printf(" (SALVAGED)\n"); 544 idesc->id_fix = FIX; 545 return (ALTERED); 546 } 547 if (reply("SALVAGE") == 0) { 548 idesc->id_fix = NOFIX; 549 return (0); 550 } 551 idesc->id_fix = FIX; 552 return (ALTERED); 553 554 case FIX: 555 return (ALTERED); 556 557 case NOFIX: 558 case IGNORE: 559 return (0); 560 561 default: 562 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); 563 } 564 /* NOTREACHED */ 565 } 566