1 /* $OpenBSD: utilities.c,v 1.55 2023/03/08 04:43:06 guenther Exp $ */ 2 /* $NetBSD: utilities.c,v 1.18 1996/09/27 22:45:20 christos Exp $ */ 3 4 /* 5 * Copyright (c) 1980, 1986, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> /* DEV_BSIZE isset setbit clrbit */ 34 #include <sys/time.h> 35 #include <sys/uio.h> 36 #include <ufs/ufs/dinode.h> 37 #include <ufs/ufs/dir.h> 38 #include <ufs/ffs/fs.h> 39 #include <signal.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <ctype.h> 44 #include <unistd.h> 45 #include <limits.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <paths.h> 49 50 #include "fsutil.h" 51 #include "fsck.h" 52 #include "extern.h" 53 54 long diskreads, totalreads; /* Disk cache statistics */ 55 static struct bufarea cgblk; /* backup buffer for cylinder group blocks */ 56 57 static void rwerror(char *, daddr_t); 58 59 int 60 ftypeok(union dinode *dp) 61 { 62 switch (DIP(dp, di_mode) & IFMT) { 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 default: 72 if (debug) 73 printf("bad file type 0%o\n", DIP(dp, di_mode)); 74 return (0); 75 } 76 } 77 78 int 79 reply(char *question) 80 { 81 int persevere, c; 82 83 if (preen) 84 pfatal("INTERNAL ERROR: GOT TO reply()"); 85 persevere = !strcmp(question, "CONTINUE"); 86 printf("\n"); 87 if (!persevere && (nflag || fswritefd < 0)) { 88 printf("%s? no\n\n", question); 89 resolved = 0; 90 return (0); 91 } 92 if (yflag || (persevere && nflag)) { 93 printf("%s? yes\n\n", question); 94 return (1); 95 } 96 97 do { 98 printf("%s? [Fyn?] ", question); 99 (void) fflush(stdout); 100 c = getc(stdin); 101 if (c == 'F') { 102 yflag = 1; 103 return (1); 104 } 105 while (c != '\n' && getc(stdin) != '\n') { 106 if (feof(stdin)) { 107 resolved = 0; 108 return (0); 109 } 110 } 111 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 112 printf("\n"); 113 if (c == 'y' || c == 'Y') 114 return (1); 115 resolved = 0; 116 return (0); 117 } 118 119 /* 120 * Look up state information for an inode. 121 */ 122 struct inostat * 123 inoinfo(ino_t inum) 124 { 125 static struct inostat unallocated = { USTATE, 0, 0 }; 126 struct inostatlist *ilp; 127 int iloff; 128 129 if (inum > maxino) 130 errexit("inoinfo: inumber %llu out of range", 131 (unsigned long long)inum); 132 ilp = &inostathead[inum / sblock.fs_ipg]; 133 iloff = inum % sblock.fs_ipg; 134 if (iloff >= ilp->il_numalloced) 135 return (&unallocated); 136 return (&ilp->il_stat[iloff]); 137 } 138 139 /* 140 * Malloc buffers and set up cache. 141 */ 142 void 143 bufinit(void) 144 { 145 struct bufarea *bp; 146 long bufcnt, i; 147 char *bufp; 148 149 pbp = pdirbp = NULL; 150 bufp = malloc((unsigned int)sblock.fs_bsize); 151 if (bufp == 0) 152 errexit("cannot allocate buffer pool\n"); 153 cgblk.b_un.b_buf = bufp; 154 initbarea(&cgblk); 155 bufhead.b_next = bufhead.b_prev = &bufhead; 156 bufcnt = MAXBUFSPACE / sblock.fs_bsize; 157 if (bufcnt < MINBUFS) 158 bufcnt = MINBUFS; 159 for (i = 0; i < bufcnt; i++) { 160 bp = malloc(sizeof(struct bufarea)); 161 bufp = malloc((unsigned int)sblock.fs_bsize); 162 if (bp == NULL || bufp == NULL) { 163 free(bp); 164 free(bufp); 165 if (i >= MINBUFS) 166 break; 167 errexit("cannot allocate buffer pool\n"); 168 } 169 bp->b_un.b_buf = bufp; 170 bp->b_prev = &bufhead; 171 bp->b_next = bufhead.b_next; 172 bufhead.b_next->b_prev = bp; 173 bufhead.b_next = bp; 174 initbarea(bp); 175 } 176 bufhead.b_size = i; /* save number of buffers */ 177 } 178 179 /* 180 * Manage cylinder group buffers. 181 */ 182 static struct bufarea *cgbufs; /* header for cylinder group cache */ 183 static int flushtries; /* number of tries to reclaim memory */ 184 struct bufarea * 185 cglookup(u_int cg) 186 { 187 struct bufarea *cgbp; 188 struct cg *cgp; 189 190 if (cgbufs == NULL) { 191 cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea)); 192 if (cgbufs == NULL) 193 errexit("cannot allocate cylinder group buffers"); 194 } 195 cgbp = &cgbufs[cg]; 196 if (cgbp->b_un.b_cg != NULL) 197 return (cgbp); 198 cgp = NULL; 199 if (flushtries == 0) 200 cgp = malloc((unsigned int)sblock.fs_cgsize); 201 if (cgp == NULL) { 202 getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 203 return (&cgblk); 204 } 205 cgbp->b_un.b_cg = cgp; 206 initbarea(cgbp); 207 getblk(cgbp, cgtod(&sblock, cg), sblock.fs_cgsize); 208 return (cgbp); 209 } 210 211 212 /* 213 * Manage a cache of directory blocks. 214 */ 215 struct bufarea * 216 getdatablk(daddr_t blkno, long size) 217 { 218 struct bufarea *bp; 219 220 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 221 if (bp->b_bno == fsbtodb(&sblock, blkno)) 222 goto foundit; 223 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 224 if ((bp->b_flags & B_INUSE) == 0) 225 break; 226 if (bp == &bufhead) 227 errexit("deadlocked buffer pool\n"); 228 getblk(bp, blkno, size); 229 /* FALLTHROUGH */ 230 foundit: 231 totalreads++; 232 bp->b_prev->b_next = bp->b_next; 233 bp->b_next->b_prev = bp->b_prev; 234 bp->b_prev = &bufhead; 235 bp->b_next = bufhead.b_next; 236 bufhead.b_next->b_prev = bp; 237 bufhead.b_next = bp; 238 bp->b_flags |= B_INUSE; 239 return (bp); 240 } 241 242 void 243 getblk(struct bufarea *bp, daddr_t blk, long size) 244 { 245 daddr_t dblk; 246 247 dblk = fsbtodb(&sblock, blk); 248 if (bp->b_bno != dblk) { 249 flush(fswritefd, bp); 250 diskreads++; 251 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); 252 bp->b_bno = dblk; 253 bp->b_size = size; 254 } 255 } 256 257 void 258 flush(int fd, struct bufarea *bp) 259 { 260 int i, j; 261 262 if (!bp->b_dirty) 263 return; 264 if (bp->b_errs != 0) 265 pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n", 266 (bp->b_errs == bp->b_size / DEV_BSIZE) ? "" : "PARTIALLY ", 267 (long long)bp->b_bno); 268 bp->b_dirty = 0; 269 bp->b_errs = 0; 270 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 271 if (bp != &sblk) 272 return; 273 for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 274 bwrite(fswritefd, (char *)sblock.fs_csp + i, 275 fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 276 sblock.fs_cssize - i < sblock.fs_bsize ? 277 sblock.fs_cssize - i : sblock.fs_bsize); 278 } 279 } 280 281 static void 282 rwerror(char *mesg, daddr_t blk) 283 { 284 285 if (preen == 0) 286 printf("\n"); 287 pfatal("CANNOT %s: BLK %lld", mesg, (long long)blk); 288 if (reply("CONTINUE") == 0) 289 errexit("Program terminated\n"); 290 } 291 292 void 293 ckfini(int markclean) 294 { 295 struct bufarea *bp, *nbp; 296 int cnt = 0; 297 sigset_t oset, nset; 298 int64_t sblockloc; 299 300 sigemptyset(&nset); 301 sigaddset(&nset, SIGINT); 302 sigprocmask(SIG_BLOCK, &nset, &oset); 303 304 if (fswritefd < 0) { 305 (void)close(fsreadfd); 306 fsreadfd = -1; 307 sigprocmask(SIG_SETMASK, &oset, NULL); 308 return; 309 } 310 if (sblock.fs_magic == FS_UFS1_MAGIC) { 311 sblockloc = SBLOCK_UFS1; 312 sblock.fs_ffs1_time = sblock.fs_time; 313 sblock.fs_ffs1_size = sblock.fs_size; 314 sblock.fs_ffs1_dsize = sblock.fs_dsize; 315 sblock.fs_ffs1_csaddr = sblock.fs_csaddr; 316 sblock.fs_ffs1_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir; 317 sblock.fs_ffs1_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree; 318 sblock.fs_ffs1_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree; 319 sblock.fs_ffs1_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree; 320 /* Force update on next mount */ 321 sblock.fs_ffs1_flags &= ~FS_FLAGS_UPDATED; 322 } else 323 sblockloc = SBLOCK_UFS2; 324 flush(fswritefd, &sblk); 325 if (havesb && sblk.b_bno != sblockloc / DEV_BSIZE && !preen && 326 reply("UPDATE STANDARD SUPERBLOCK")) { 327 sblk.b_bno = sblockloc / DEV_BSIZE; 328 sbdirty(); 329 flush(fswritefd, &sblk); 330 } 331 flush(fswritefd, &cgblk); 332 free(cgblk.b_un.b_buf); 333 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 334 cnt++; 335 flush(fswritefd, bp); 336 nbp = bp->b_prev; 337 free(bp->b_un.b_buf); 338 free(bp); 339 } 340 if (bufhead.b_size != cnt) 341 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); 342 if (cgbufs != NULL) { 343 for (cnt = 0; cnt < sblock.fs_ncg; cnt++) { 344 if (cgbufs[cnt].b_un.b_cg == NULL) 345 continue; 346 flush(fswritefd, &cgbufs[cnt]); 347 free(cgbufs[cnt].b_un.b_cg); 348 } 349 free(cgbufs); 350 } 351 pbp = pdirbp = NULL; 352 if (markclean && (sblock.fs_clean & FS_ISCLEAN) == 0) { 353 /* 354 * Mark the file system as clean, and sync the superblock. 355 */ 356 if (preen) 357 pwarn("MARKING FILE SYSTEM CLEAN\n"); 358 else if (!reply("MARK FILE SYSTEM CLEAN")) 359 markclean = 0; 360 if (markclean) { 361 sblock.fs_clean = FS_ISCLEAN; 362 sbdirty(); 363 flush(fswritefd, &sblk); 364 } 365 } 366 if (debug) 367 printf("cache missed %ld of %ld (%d%%)\n", diskreads, 368 totalreads, (int)(diskreads * 100 / totalreads)); 369 (void)close(fsreadfd); 370 fsreadfd = -1; 371 (void)close(fswritefd); 372 fswritefd = -1; 373 sigprocmask(SIG_SETMASK, &oset, NULL); 374 } 375 376 int 377 bread(int fd, char *buf, daddr_t blk, long size) 378 { 379 char *cp; 380 int i, errs; 381 off_t offset; 382 383 offset = blk; 384 offset *= DEV_BSIZE; 385 if (pread(fd, buf, size, offset) == size) 386 return (0); 387 rwerror("READ", blk); 388 errs = 0; 389 memset(buf, 0, (size_t)size); 390 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); 391 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { 392 if (pread(fd, cp, secsize, offset + i) != secsize) { 393 if (secsize != DEV_BSIZE) 394 printf(" %lld (%lld),", 395 (long long)(offset + i) / secsize, 396 (long long)blk + i / DEV_BSIZE); 397 else 398 printf(" %lld,", (long long)blk + 399 i / DEV_BSIZE); 400 errs++; 401 } 402 } 403 printf("\n"); 404 return (errs); 405 } 406 407 void 408 bwrite(int fd, char *buf, daddr_t blk, long size) 409 { 410 int i; 411 char *cp; 412 off_t offset; 413 414 if (fd < 0) 415 return; 416 offset = blk; 417 offset *= DEV_BSIZE; 418 if (pwrite(fd, buf, size, offset) == size) { 419 fsmodified = 1; 420 return; 421 } 422 rwerror("WRITE", blk); 423 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 424 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) 425 if (pwrite(fd, cp, secsize, offset + i) != secsize) { 426 if (secsize != DEV_BSIZE) 427 printf(" %lld (%lld),", 428 (long long)(offset + i) / secsize, 429 (long long)blk + i / DEV_BSIZE); 430 else 431 printf(" %lld,", (long long)blk + 432 i / DEV_BSIZE); 433 } 434 printf("\n"); 435 return; 436 } 437 438 /* 439 * allocate a data block with the specified number of fragments 440 */ 441 daddr_t 442 allocblk(int frags) 443 { 444 daddr_t i, baseblk; 445 int j, k, cg; 446 struct bufarea *cgbp; 447 struct cg *cgp; 448 449 if (frags <= 0 || frags > sblock.fs_frag) 450 return (0); 451 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { 452 for (j = 0; j <= sblock.fs_frag - frags; j++) { 453 if (testbmap(i + j)) 454 continue; 455 for (k = 1; k < frags; k++) 456 if (testbmap(i + j + k)) 457 break; 458 if (k < frags) { 459 j += k; 460 continue; 461 } 462 cg = dtog(&sblock, i + j); 463 cgbp = cglookup(cg); 464 cgp = cgbp->b_un.b_cg; 465 if (!cg_chkmagic(cgp)) 466 pfatal("CG %d: BAD MAGIC NUMBER\n", cg); 467 baseblk = dtogd(&sblock, i + j); 468 469 for (k = 0; k < frags; k++) { 470 setbmap(i + j + k); 471 clrbit(cg_blksfree(cgp), baseblk + k); 472 } 473 n_blks += frags; 474 if (frags == sblock.fs_frag) 475 cgp->cg_cs.cs_nbfree--; 476 else 477 cgp->cg_cs.cs_nffree -= frags; 478 return (i + j); 479 } 480 } 481 return (0); 482 } 483 484 /* 485 * Free a previously allocated block 486 */ 487 void 488 freeblk(daddr_t blkno, int frags) 489 { 490 struct inodesc idesc; 491 492 idesc.id_blkno = blkno; 493 idesc.id_numfrags = frags; 494 (void)pass4check(&idesc); 495 } 496 497 /* 498 * Find a pathname 499 */ 500 void 501 getpathname(char *namebuf, size_t namebuflen, ino_t curdir, ino_t ino) 502 { 503 int len; 504 char *cp; 505 struct inodesc idesc; 506 static int busy = 0; 507 508 if (curdir == ino && ino == ROOTINO) { 509 (void)strlcpy(namebuf, "/", namebuflen); 510 return; 511 } 512 if (busy || 513 (GET_ISTATE(curdir) != DSTATE && GET_ISTATE(curdir) != DFOUND)) { 514 (void)strlcpy(namebuf, "?", namebuflen); 515 return; 516 } 517 busy = 1; 518 memset(&idesc, 0, sizeof(struct inodesc)); 519 idesc.id_type = DATA; 520 idesc.id_fix = IGNORE; 521 cp = &namebuf[PATH_MAX - 1]; 522 *cp = '\0'; 523 if (curdir != ino) { 524 idesc.id_parent = curdir; 525 goto namelookup; 526 } 527 while (ino != ROOTINO) { 528 idesc.id_number = ino; 529 idesc.id_func = findino; 530 idesc.id_name = ".."; 531 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) 532 break; 533 namelookup: 534 idesc.id_number = idesc.id_parent; 535 idesc.id_parent = ino; 536 idesc.id_func = findname; 537 idesc.id_name = namebuf; 538 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) 539 break; 540 len = strlen(namebuf); 541 cp -= len; 542 memmove(cp, namebuf, (size_t)len); 543 *--cp = '/'; 544 if (cp < &namebuf[MAXNAMLEN]) 545 break; 546 ino = idesc.id_number; 547 } 548 busy = 0; 549 if (ino != ROOTINO) 550 *--cp = '?'; 551 memmove(namebuf, cp, (size_t)(&namebuf[PATH_MAX] - cp)); 552 } 553 554 void 555 catch(int signo) 556 { 557 ckfini(0); /* XXX signal race */ 558 _exit(12); 559 } 560 561 /* 562 * When preening, allow a single quit to signal 563 * a special exit after filesystem checks complete 564 * so that reboot sequence may be interrupted. 565 */ 566 void 567 catchquit(int signo) 568 { 569 extern volatile sig_atomic_t returntosingle; 570 static const char message[] = 571 "returning to single-user after filesystem check\n"; 572 573 write(STDOUT_FILENO, message, sizeof(message)-1); 574 returntosingle = 1; 575 (void)signal(SIGQUIT, SIG_DFL); 576 } 577 578 /* 579 * Ignore a single quit signal; wait and flush just in case. 580 * Used by child processes in preen. 581 */ 582 void 583 voidquit(int signo) 584 { 585 int save_errno = errno; 586 587 sleep(1); 588 (void)signal(SIGQUIT, SIG_IGN); 589 (void)signal(SIGQUIT, SIG_DFL); 590 errno = save_errno; 591 } 592 593 /* 594 * determine whether an inode should be fixed. 595 */ 596 int 597 dofix(struct inodesc *idesc, char *msg) 598 { 599 switch (idesc->id_fix) { 600 601 case DONTKNOW: 602 if (idesc->id_type == DATA) 603 direrror(idesc->id_number, msg); 604 else 605 pwarn("%s", msg); 606 if (preen) { 607 printf(" (SALVAGED)\n"); 608 idesc->id_fix = FIX; 609 return (ALTERED); 610 } 611 if (reply("SALVAGE") == 0) { 612 idesc->id_fix = NOFIX; 613 return (0); 614 } 615 idesc->id_fix = FIX; 616 return (ALTERED); 617 618 case FIX: 619 return (ALTERED); 620 621 case NOFIX: 622 case IGNORE: 623 return (0); 624 625 default: 626 errexit("UNKNOWN INODESC FIX MODE %u\n", idesc->id_fix); 627 } 628 /* NOTREACHED */ 629 } 630 631 int (* info_fn)(char *, size_t) = NULL; 632 char *info_filesys = "?"; 633 634 void 635 catchinfo(int signo) 636 { 637 static int info_fd; 638 int save_errno = errno; 639 struct iovec iov[4]; 640 char buf[1024]; 641 642 if (signo == 0) { 643 info_fd = open(_PATH_TTY, O_WRONLY); 644 signal(SIGINFO, catchinfo); 645 } else if (info_fd > 0 && info_fn != NULL && info_fn(buf, sizeof buf)) { 646 iov[0].iov_base = info_filesys; 647 iov[0].iov_len = strlen(info_filesys); 648 iov[1].iov_base = ": "; 649 iov[1].iov_len = sizeof ": " - 1; 650 iov[2].iov_base = buf; 651 iov[2].iov_len = strlen(buf); 652 iov[3].iov_base = "\n"; 653 iov[3].iov_len = sizeof "\n" - 1; 654 655 writev(info_fd, iov, 4); 656 } 657 errno = save_errno; 658 } 659 /* 660 * Attempt to flush a cylinder group cache entry. 661 * Return whether the flush was successful. 662 */ 663 static int 664 flushentry(void) 665 { 666 struct bufarea *cgbp; 667 668 if (flushtries == sblock.fs_ncg || cgbufs == NULL) 669 return (0); 670 cgbp = &cgbufs[flushtries++]; 671 if (cgbp->b_un.b_cg == NULL) 672 return (0); 673 flush(fswritefd, cgbp); 674 free(cgbp->b_un.b_buf); 675 cgbp->b_un.b_buf = NULL; 676 return (1); 677 } 678 679 /* 680 * Wrapper for malloc() that flushes the cylinder group cache to try 681 * to get space. 682 */ 683 void * 684 Malloc(size_t size) 685 { 686 void *retval; 687 688 while ((retval = malloc(size)) == NULL) 689 if (flushentry() == 0) 690 break; 691 return (retval); 692 } 693 694 /* 695 * Wrapper for calloc() that flushes the cylinder group cache to try 696 * to get space. 697 */ 698 void* 699 Calloc(size_t cnt, size_t size) 700 { 701 void *retval; 702 703 while ((retval = calloc(cnt, size)) == NULL) 704 if (flushentry() == 0) 705 break; 706 return (retval); 707 } 708 709 /* 710 * Wrapper for reallocarray() that flushes the cylinder group cache to try 711 * to get space. 712 */ 713 void* 714 Reallocarray(void *p, size_t cnt, size_t size) 715 { 716 void *retval; 717 718 while ((retval = reallocarray(p, cnt, size)) == NULL) 719 if (flushentry() == 0) 720 break; 721 return (retval); 722 } 723